Skip to content

Commit 4075381

Browse files
Real world scenario ETL - Facade pattern
1 parent 7c5308d commit 4075381

File tree

2 files changed

+114
-0
lines changed

2 files changed

+114
-0
lines changed

src/Facade/RealWorld/index.ts

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import * as fs from "fs/promises";
2+
3+
/**
4+
* Example Facade pattern for a ETL process.
5+
* In this example I have created three subsystems.
6+
* The first one is the Loader (DataSource), which is a file system.
7+
* The second one is the Parser (DataTransformer), which is a string parser.
8+
* The third one is the Writer (DataSink), which is a file system.
9+
*
10+
* To keep the example simple, in the loader I'm not doing input validation.
11+
* In a real world scenario, I would do it creating a validation layer
12+
* on the extractor and passing the parsed result to the transformer.
13+
*/
14+
15+
type Map = { [key: string]: any };
16+
17+
interface Extractor {
18+
extract(): Promise<string>;
19+
}
20+
21+
interface Transformer {
22+
transform(input: string): Map;
23+
}
24+
25+
interface Loader {
26+
load(input: Map): Promise<any>;
27+
}
28+
29+
class FileExtractor implements Extractor {
30+
filepath: string;
31+
constructor(filepath: string) {
32+
this.filepath = filepath;
33+
}
34+
35+
public async extract() {
36+
//load file from this.filepath
37+
return fs.readFile(this.filepath, "utf8");
38+
}
39+
}
40+
41+
class FileLoader implements Loader {
42+
filepath: string;
43+
constructor(filepath: string) {
44+
this.filepath = filepath;
45+
}
46+
public async load(input: Map) {
47+
return fs.writeFile(this.filepath, JSON.stringify(input, undefined, 4));
48+
}
49+
}
50+
51+
class FileTransformer implements Transformer {
52+
public transform(input: string): Map {
53+
let result: Map = {};
54+
55+
input.split("\n").forEach((line) => {
56+
if (line.trim().length === 0) return;
57+
58+
const [key] = line.split(",");
59+
if (typeof result === "undefined") {
60+
}
61+
if (typeof result[key] === "undefined") {
62+
result[key] = 0;
63+
}
64+
result[key] = result[key] + 1;
65+
});
66+
67+
return result;
68+
}
69+
}
70+
71+
/**
72+
* The Facade class is the main class of the Facade pattern.
73+
* It's responsible for creating the subsystems and calling their methods.
74+
* I'm injecting the subsystems in the constructor.
75+
* In the process method I'm calling the extract, transform and load methods of the subsystems.
76+
*/
77+
class ETLProcessor {
78+
extractor: Extractor;
79+
transformer: Transformer;
80+
loader: Loader;
81+
82+
constructor(extractor: Extractor, transformer: Transformer, loader: Loader) {
83+
this.extractor = extractor;
84+
this.transformer = transformer;
85+
this.loader = loader;
86+
}
87+
88+
public async process() {
89+
const input = await this.extractor.extract();
90+
const transformed = this.transformer.transform(input);
91+
return this.loader.load(transformed);
92+
}
93+
}
94+
95+
const processor = new ETLProcessor(
96+
new FileExtractor("input.txt"),
97+
new FileTransformer(),
98+
new FileLoader("output.json")
99+
);
100+
101+
processor.process().then(() => {
102+
console.log("Process completed");
103+
});

src/Facade/RealWorld/input.txt

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
3+
4+
5+
6+
7+
8+
9+
10+
11+

0 commit comments

Comments
 (0)