├── .nvmrc
├── packages
├── core
│ ├── src
│ │ ├── index.ts
│ │ ├── base
│ │ │ └── usecase.interface.ts
│ │ └── tests
│ │ │ └── example.test.ts
│ ├── .gitignore
│ ├── .prettierrc
│ ├── tsconfig.build.json
│ ├── jest.config.js
│ ├── tsconfig.json
│ ├── rollup.config.js
│ └── package.json
├── data
│ ├── src
│ │ ├── index.ts
│ │ └── tests
│ │ │ └── .gitkeep
│ ├── .prettierrc
│ ├── tsconfig.build.json
│ ├── jest.config.js
│ ├── tsconfig.json
│ ├── rollup.config.js
│ └── package.json
├── di
│ ├── src
│ │ └── index.ts
│ ├── .prettierrc
│ ├── tsconfig.json
│ ├── package.json
│ └── rollup.config.js
└── presentation
│ ├── src
│ ├── di
│ │ └── .gitkeep
│ ├── assets
│ │ └── .gitkeep
│ ├── app
│ │ ├── app.component.scss
│ │ ├── app.component.html
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ └── app.component.spec.ts
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── styles.scss
│ ├── favicon.ico
│ ├── tests
│ │ └── example.test.ts
│ ├── index.html
│ ├── main.ts
│ ├── test.ts
│ └── polyfills.ts
│ ├── .prettierrc
│ ├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ └── tasks.json
│ ├── jest.config.js
│ ├── .editorconfig
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ ├── .browserslistrc
│ ├── .gitignore
│ ├── tsconfig.json
│ ├── README.md
│ ├── package.json
│ ├── karma.conf.js
│ └── angular.json
├── .prettierrc
├── lerna.json
├── .gitignore
├── package.json
├── tsconfig.json
├── nx.json
├── typescript-clean-architecture.code-workspace
└── README.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | v16.13.0
2 |
--------------------------------------------------------------------------------
/packages/core/src/index.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/data/src/index.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/di/src/index.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/data/src/tests/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/presentation/src/di/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/presentation/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/core/.gitignore:
--------------------------------------------------------------------------------
1 | tsconfig.tsbuildinfo
--------------------------------------------------------------------------------
/packages/presentation/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "printWidth": 120
4 | }
--------------------------------------------------------------------------------
/packages/data/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "printWidth": 120
4 | }
--------------------------------------------------------------------------------
/packages/di/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "printWidth": 120
4 | }
--------------------------------------------------------------------------------
/packages/presentation/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 | Hello World!
3 |
--------------------------------------------------------------------------------
/packages/presentation/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "printWidth": 120
4 | }
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": ["packages/*"],
3 | "useNx": true,
4 | "version": "0.0.0"
5 | }
6 |
--------------------------------------------------------------------------------
/packages/core/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "printWidth": 120,
4 | "singleQuote": true
5 | }
--------------------------------------------------------------------------------
/packages/core/src/base/usecase.interface.ts:
--------------------------------------------------------------------------------
1 | export interface Usecase {
2 | execute(...args: any[]): T;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/presentation/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/packages/presentation/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | **/build
3 | **/types
4 |
5 | # Dependency Cruiser Artifacts
6 | dependencygraph.svg
7 | *.log
--------------------------------------------------------------------------------
/packages/core/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 |
4 | "exclude": ["node_modules", "src/tests"],
5 | }
6 |
--------------------------------------------------------------------------------
/packages/data/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 |
4 | "exclude": ["node_modules", "src/tests"],
5 | }
6 |
--------------------------------------------------------------------------------
/packages/presentation/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aziznal/typescript-clean-architecture/HEAD/packages/presentation/src/favicon.ico
--------------------------------------------------------------------------------
/packages/core/src/tests/example.test.ts:
--------------------------------------------------------------------------------
1 | describe("Example Test", () => {
2 | it("Adds two and two together", () => {
3 | expect(2 + 2).toBe(4);
4 | });
5 | });
6 |
--------------------------------------------------------------------------------
/packages/presentation/src/tests/example.test.ts:
--------------------------------------------------------------------------------
1 | describe('Example Test', () => {
2 | it('Adds two and two together', () => {
3 | expect(2 + 2).toBe(4);
4 | });
5 | });
6 |
--------------------------------------------------------------------------------
/packages/presentation/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
3 | "recommendations": ["angular.ng-template"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/core/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transform: { '^.+\\.ts?$': 'ts-jest' },
3 | testEnvironment: 'node',
4 | testRegex: '.*\\.(test)?\\.(ts)$',
5 | moduleFileExtensions: ['ts', 'js'],
6 | };
--------------------------------------------------------------------------------
/packages/data/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transform: { '^.+\\.ts?$': 'ts-jest' },
3 | testEnvironment: 'node',
4 | testRegex: '.*\\.(test)?\\.(ts)$',
5 | moduleFileExtensions: ['ts', 'js'],
6 | };
--------------------------------------------------------------------------------
/packages/presentation/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transform: { '^.+\\.ts?$': 'ts-jest' },
3 | testEnvironment: 'node',
4 | testRegex: '.*\\.(test)?\\.(ts)$',
5 | moduleFileExtensions: ['ts', 'js'],
6 | };
--------------------------------------------------------------------------------
/packages/presentation/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 4
8 | trim_trailing_whitespace = true
9 |
10 | [*.ts]
11 | quote_type = single
12 |
13 | [*.md]
14 | max_line_length = off
15 | trim_trailing_whitespace = false
16 |
--------------------------------------------------------------------------------
/packages/presentation/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.scss'],
7 | })
8 | export class AppComponent implements OnInit {
9 | constructor() {}
10 |
11 | ngOnInit(): void {}
12 | }
13 |
--------------------------------------------------------------------------------
/packages/presentation/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HelloWorld
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/presentation/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/app",
6 | "types": []
7 | },
8 | "files": [
9 | "src/main.ts",
10 | "src/polyfills.ts"
11 | ],
12 | "include": [
13 | "src/**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-clean-architecture",
3 | "license": "ISC",
4 | "scripts": {
5 | "prestart": "npm install && lerna bootstrap",
6 | "start": "lerna run start",
7 | "test": "lerna run test",
8 | "build": "lerna run build",
9 | "graph": "nx graph"
10 | },
11 | "devDependencies": {
12 | "lerna": "^5.1.8",
13 | "nx": "^14.4.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/presentation/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/spec",
6 | "types": [
7 | "jasmine"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "sourceMap": true,
5 |
6 | "allowSyntheticDefaultImports": true,
7 |
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 |
11 | "noImplicitAny": true,
12 | "noImplicitOverride": true,
13 | "noImplicitReturns": true,
14 | "forceConsistentCasingInFileNames": true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/nx.json:
--------------------------------------------------------------------------------
1 | {
2 | "tasksRunnerOptions": {
3 | "default": {
4 | "runner": "nx/tasks-runners/default",
5 | "options": {
6 | "cacheableOperations": ["build", "test"]
7 | }
8 | }
9 | },
10 | "targetDefaults": {
11 | "build": {
12 | "dependsOn": ["^build"]
13 | },
14 | "start": {
15 | "dependsOn": ["^build"]
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/presentation/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 |
6 | import { environment } from './environments/environment';
7 |
8 | if (environment.production) {
9 | enableProdMode();
10 | }
11 |
12 | platformBrowserDynamic()
13 | .bootstrapModule(AppModule)
14 | .catch((err) => console.error(err));
15 |
--------------------------------------------------------------------------------
/packages/presentation/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
4 | import { BrowserModule } from '@angular/platform-browser';
5 |
6 | import { AppComponent } from './app.component';
7 |
8 | @NgModule({
9 | declarations: [AppComponent],
10 |
11 | imports: [BrowserModule, CommonModule, FormsModule, ReactiveFormsModule],
12 |
13 | providers: [],
14 |
15 | bootstrap: [AppComponent],
16 | })
17 | export class AppModule {}
18 |
--------------------------------------------------------------------------------
/packages/presentation/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
3 | "version": "0.2.0",
4 | "configurations": [
5 | {
6 | "name": "ng serve",
7 | "type": "pwa-chrome",
8 | "request": "launch",
9 | "preLaunchTask": "npm: start",
10 | "url": "http://localhost:4200/"
11 | },
12 | {
13 | "name": "ng test",
14 | "type": "chrome",
15 | "request": "launch",
16 | "preLaunchTask": "npm: test",
17 | "url": "http://localhost:9876/debug.html"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/typescript-clean-architecture.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "name": "root",
5 | "path": "."
6 | },
7 | {
8 | "name": "core",
9 | "path": "packages/core"
10 | },
11 | {
12 | "name": "data",
13 | "path": "packages/data"
14 | },
15 | {
16 | "name": "di",
17 | "path": "packages/di"
18 | },
19 | {
20 | "name": "presentation",
21 | "path": "packages/presentation"
22 | }
23 | ],
24 | "settings": {}
25 | }
26 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 |
4 | "include": ["src/**/*.ts"],
5 |
6 | "exclude": ["node_modules"],
7 |
8 | "compilerOptions": {
9 | "baseUrl": ".",
10 | "rootDir": ".",
11 |
12 | "module": "ES2020",
13 | "target": "ES2020",
14 | "moduleResolution": "node",
15 |
16 | "outDir": "build/",
17 |
18 | "declaration": true,
19 | "declarationMap": true,
20 | "sourceMap": true,
21 |
22 | "plugins": [
23 | {
24 | "name": "@rollup/plugin-typescript"
25 | }
26 | ]
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/data/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 |
4 | "include": ["src/**/*.ts"],
5 |
6 | "exclude": ["node_modules"],
7 |
8 | "compilerOptions": {
9 | "baseUrl": ".",
10 | "rootDir": ".",
11 |
12 | "module": "ES2020",
13 | "target": "ES2020",
14 | "moduleResolution": "node",
15 |
16 | "outDir": "build/",
17 |
18 | "declaration": true,
19 | "declarationMap": true,
20 | "sourceMap": true,
21 |
22 | "plugins": [
23 | {
24 | "name": "@rollup/plugin-typescript"
25 | }
26 | ]
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/presentation/.browserslistrc:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # For the full list of supported browsers by the Angular framework, please see:
6 | # https://angular.io/guide/browser-support
7 |
8 | # You can see what browsers were selected by your queries by running:
9 | # npx browserslist
10 |
11 | last 1 Chrome version
12 | last 1 Firefox version
13 | last 2 Edge major versions
14 | last 2 Safari major versions
15 | last 2 iOS major versions
16 | Firefox ESR
17 |
--------------------------------------------------------------------------------
/packages/presentation/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/packages/presentation/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/packages/di/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 |
4 | "include": ["src/**/*.ts"],
5 |
6 | "exclude": ["node_modules"],
7 |
8 | "compilerOptions": {
9 | "baseUrl": ".",
10 | "rootDir": ".",
11 |
12 | "module": "ES2020",
13 | "target": "ES2020",
14 | "moduleResolution": "node",
15 |
16 | "lib": ["ES6", "DOM"],
17 |
18 | "experimentalDecorators": true,
19 | "emitDecoratorMetadata": true,
20 |
21 | "declaration": true,
22 | "declarationMap": true,
23 | "sourceMap": true,
24 |
25 | "outDir": "build/",
26 |
27 | "plugins": [
28 | {
29 | "name": "@rollup/plugin-typescript"
30 | }
31 | ]
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/di/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "di",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "build": "tsc && npm run rollup",
6 | "build:watch": "nodemon -e ts --exec \"npm run build\" --ignore build/** --ignore tests/**",
7 | "rollup": "rollup -c"
8 | },
9 | "license": "ISC",
10 | "dependencies": {
11 | "core": "*",
12 | "data": "*"
13 | },
14 | "devDependencies": {
15 | "@rollup/plugin-typescript": "^8.3.3",
16 | "@types/node": "^18.0.0",
17 | "typescript": "^4.7.4",
18 | "rollup": "^2.77.0",
19 | "rollup-plugin-dts": "4.2.2",
20 | "nodemon": "^2.0.19",
21 | "tslib": "^2.4.0"
22 | },
23 | "main": "build/src/index.js",
24 | "types": "build/src/index.d.ts"
25 | }
26 |
--------------------------------------------------------------------------------
/packages/presentation/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | (id: string): T;
13 | keys(): string[];
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting(),
21 | );
22 |
23 | // Then we find all the tests.
24 | const context = require.context('./', true, /\.spec\.ts$/);
25 | // And load the modules.
26 | context.keys().forEach(context);
27 |
--------------------------------------------------------------------------------
/packages/core/rollup.config.js:
--------------------------------------------------------------------------------
1 | import typescript from '@rollup/plugin-typescript';
2 | import dts from 'rollup-plugin-dts';
3 |
4 | import tsConfig from './tsconfig.json';
5 |
6 | const config = [
7 | {
8 | input: "src/index.ts",
9 | output: [{ file: "build/src/index.js", sourcemap: true }],
10 | plugins: [
11 | typescript(
12 | {
13 | sourceMap: tsConfig.compilerOptions.sourceMap
14 | }
15 | )
16 | ]
17 | },
18 | {
19 | input: 'build/src/index.d.ts',
20 | output: [{ file: "build/src/index.d.ts", "format": "es" }],
21 | plugins: [
22 | dts(
23 | {
24 | compilerOptions: {
25 | baseUrl: tsConfig.compilerOptions.baseUrl
26 | }
27 | }
28 | )
29 | ]
30 | },
31 | ]
32 |
33 | export default config;
--------------------------------------------------------------------------------
/packages/data/rollup.config.js:
--------------------------------------------------------------------------------
1 | import typescript from '@rollup/plugin-typescript';
2 | import dts from 'rollup-plugin-dts';
3 |
4 | import tsConfig from './tsconfig.json';
5 |
6 | const config = [
7 | {
8 | input: "src/index.ts",
9 | output: [{ file: "build/src/index.js", sourcemap: true }],
10 | plugins: [
11 | typescript(
12 | {
13 | sourceMap: tsConfig.compilerOptions.sourceMap,
14 | }
15 | )
16 | ]
17 | },
18 | {
19 | input: 'build/src/index.d.ts',
20 | output: [{ file: "build/src/index.d.ts", "format": "es" }],
21 | plugins: [
22 | dts(
23 | {
24 | compilerOptions: {
25 | baseUrl: tsConfig.compilerOptions.baseUrl
26 | }
27 | }
28 | )
29 | ]
30 | },
31 | ]
32 |
33 | export default config;
--------------------------------------------------------------------------------
/packages/di/rollup.config.js:
--------------------------------------------------------------------------------
1 | import typescript from '@rollup/plugin-typescript';
2 | import dts from 'rollup-plugin-dts';
3 |
4 | import tsConfig from './tsconfig.json';
5 |
6 | const config = [
7 | {
8 | input: "src/index.ts",
9 | output: [{ file: "build/src/index.js", sourcemap: true }],
10 | plugins: [
11 | typescript(
12 | {
13 | sourceMap: tsConfig.compilerOptions.sourceMap,
14 | }
15 | )
16 | ]
17 | },
18 | {
19 | input: 'build/src/index.d.ts',
20 | output: [{ file: "build/src/index.d.ts", "format": "es" }],
21 | plugins: [
22 | dts(
23 | {
24 | compilerOptions: {
25 | baseUrl: tsConfig.compilerOptions.baseUrl
26 | }
27 | }
28 | )
29 | ]
30 | },
31 | ]
32 |
33 | export default config;
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "core",
3 | "version": "1.0.0",
4 | "description": "",
5 | "scripts": {
6 | "build": "tsc --project tsconfig.build.json && npm run rollup",
7 | "build:watch": "nodemon -e ts --exec \"npm run build\" --ignore build/** --ignore tests/**",
8 | "rollup": "rollup -c",
9 | "test": "jest",
10 | "test:watch": "jest --watch"
11 | },
12 | "author": "",
13 | "license": "ISC",
14 | "devDependencies": {
15 | "@rollup/plugin-typescript": "^8.3.3",
16 | "@types/jest": "^28.1.6",
17 | "@types/node": "^18.0.0",
18 | "jest": "^28.1.3",
19 | "nodemon": "^2.0.19",
20 | "rollup": "^2.77.0",
21 | "rollup-plugin-dts": "^4.2.2",
22 | "ts-jest": "^28.0.7",
23 | "tslib": "^2.4.0",
24 | "typescript": "^4.7.4"
25 | },
26 | "main": "build/src/index.js",
27 | "types": "build/src/index.d.ts"
28 | }
29 |
--------------------------------------------------------------------------------
/packages/data/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "data",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "build": "tsc --project tsconfig.build.json && npm run rollup",
6 | "build:watch": "nodemon -e ts --exec \"npm run build\" --ignore build/** --ignore tests/**",
7 | "rollup": "rollup -c",
8 | "test": "jest",
9 | "test:watch": "jest --watch"
10 | },
11 | "license": "ISC",
12 | "dependencies": {
13 | "core": "*"
14 | },
15 | "devDependencies": {
16 | "@rollup/plugin-typescript": "^8.3.3",
17 | "@types/node": "^18.0.0",
18 | "@types/jest": "^28.1.6",
19 | "typescript": "^4.7.4",
20 | "nodemon": "^2.0.19",
21 | "rollup": "^2.77.0",
22 | "rollup-plugin-dts": "4.2.2",
23 | "ts-jest": "^28.0.7",
24 | "tslib": "^2.4.0",
25 | "jest": "^28.1.3"
26 | },
27 | "main": "build/src/index.js",
28 | "types": "build/src/index.d.ts"
29 | }
30 |
--------------------------------------------------------------------------------
/packages/presentation/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 |
4 | describe('AppComponent', () => {
5 | beforeEach(async () => {
6 | await TestBed.configureTestingModule({
7 | declarations: [
8 | AppComponent
9 | ],
10 | }).compileComponents();
11 | });
12 |
13 | it('should create the app', () => {
14 | const fixture = TestBed.createComponent(AppComponent);
15 | const app = fixture.componentInstance;
16 | expect(app).toBeTruthy();
17 | });
18 |
19 | it(`should have as title 'presentation'`, () => {
20 | const fixture = TestBed.createComponent(AppComponent);
21 | const app = fixture.componentInstance;
22 | expect(app.title).toEqual('presentation');
23 | });
24 |
25 | it('should render title', () => {
26 | const fixture = TestBed.createComponent(AppComponent);
27 | fixture.detectChanges();
28 | const compiled = fixture.nativeElement as HTMLElement;
29 | expect(compiled.querySelector('.content span')?.textContent).toContain('presentation app is running!');
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/packages/presentation/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
3 | "version": "2.0.0",
4 | "tasks": [
5 | {
6 | "type": "npm",
7 | "script": "start",
8 | "isBackground": true,
9 | "problemMatcher": {
10 | "owner": "typescript",
11 | "pattern": "$tsc",
12 | "background": {
13 | "activeOnStart": true,
14 | "beginsPattern": {
15 | "regexp": "(.*?)"
16 | },
17 | "endsPattern": {
18 | "regexp": "bundle generation complete"
19 | }
20 | }
21 | }
22 | },
23 | {
24 | "type": "npm",
25 | "script": "test",
26 | "isBackground": true,
27 | "problemMatcher": {
28 | "owner": "typescript",
29 | "pattern": "$tsc",
30 | "background": {
31 | "activeOnStart": true,
32 | "beginsPattern": {
33 | "regexp": "(.*?)"
34 | },
35 | "endsPattern": {
36 | "regexp": "bundle generation complete"
37 | }
38 | }
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/packages/presentation/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "include": ["src/**/*.ts"],
4 |
5 | "compileOnSave": false,
6 | "compilerOptions": {
7 | "baseUrl": "./",
8 | "outDir": "./dist/out-tsc",
9 | "forceConsistentCasingInFileNames": true,
10 | "strict": true,
11 | "noImplicitOverride": true,
12 | "noPropertyAccessFromIndexSignature": true,
13 | "noImplicitReturns": true,
14 | "noFallthroughCasesInSwitch": true,
15 | "sourceMap": true,
16 | "declaration": false,
17 | "downlevelIteration": true,
18 | "experimentalDecorators": true,
19 | "moduleResolution": "node",
20 | "importHelpers": true,
21 | "target": "es2020",
22 | "module": "es2020",
23 | "lib": ["es2020", "dom"],
24 |
25 | "allowSyntheticDefaultImports": true
26 | },
27 |
28 | "angularCompilerOptions": {
29 | "enableI18nLegacyMessageIdFormat": false,
30 | "strictInjectionParameters": true,
31 | "strictInputAccessModifiers": true,
32 | "strictTemplates": true
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/presentation/README.md:
--------------------------------------------------------------------------------
1 | # HelloWorld
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.1.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
28 |
--------------------------------------------------------------------------------
/packages/presentation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "presentation",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "watch": "ng build --watch --configuration development",
9 | "test": "jest",
10 | "test:watch": "jest --watch",
11 | "test:integration": "ng test"
12 | },
13 | "private": true,
14 | "dependencies": {
15 | "@angular/animations": "^14.0.0",
16 | "@angular/common": "^14.0.0",
17 | "@angular/compiler": "^14.0.0",
18 | "@angular/core": "^14.0.0",
19 | "@angular/forms": "^14.0.0",
20 | "@angular/platform-browser": "^14.0.0",
21 | "@angular/platform-browser-dynamic": "^14.0.0",
22 | "@angular/router": "^14.0.0",
23 | "rxjs": "~7.5.0",
24 | "tslib": "^2.3.0",
25 | "zone.js": "~0.11.4",
26 | "core": "*",
27 | "di": "*"
28 | },
29 | "devDependencies": {
30 | "@angular-devkit/build-angular": "^14.0.1",
31 | "@angular/cli": "~14.0.1",
32 | "@angular/compiler-cli": "^14.0.0",
33 | "@types/jasmine": "~4.0.0",
34 | "jasmine-core": "~4.1.0",
35 | "karma": "~6.3.0",
36 | "karma-chrome-launcher": "~3.1.0",
37 | "karma-coverage": "~2.2.0",
38 | "karma-jasmine": "~5.0.0",
39 | "karma-jasmine-html-reporter": "~1.7.0",
40 | "typescript": "~4.7.2",
41 | "@types/jest": "^28.1.6",
42 | "jest": "^28.1.3",
43 | "ts-jest": "^28.0.7"
44 | },
45 | "nx": {
46 | "targets": {
47 | "build": {
48 | "outputs": [
49 | "{projectRoot}/dist"
50 | ]
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/presentation/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | jasmine: {
17 | // you can add configuration options for Jasmine here
18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19 | // for example, you can disable the random execution with `random: false`
20 | // or set a specific seed with `seed: 4321`
21 | },
22 | clearContext: false // leave Jasmine Spec Runner output visible in browser
23 | },
24 | jasmineHtmlReporter: {
25 | suppressAll: true // removes the duplicated traces
26 | },
27 | coverageReporter: {
28 | dir: require('path').join(__dirname, './coverage/presentation'),
29 | subdir: '.',
30 | reporters: [
31 | { type: 'html' },
32 | { type: 'text-summary' }
33 | ]
34 | },
35 | reporters: ['progress', 'kjhtml'],
36 | port: 9876,
37 | colors: true,
38 | logLevel: config.LOG_INFO,
39 | autoWatch: true,
40 | browsers: ['Chrome'],
41 | singleRun: false,
42 | restartOnFileChange: true
43 | });
44 | };
45 |
--------------------------------------------------------------------------------
/packages/presentation/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes recent versions of Safari, Chrome (including
12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 |
51 | /***************************************************************************************************
52 | * APPLICATION IMPORTS
53 | */
54 |
--------------------------------------------------------------------------------
/packages/presentation/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "presentation": {
7 | "projectType": "application",
8 | "schematics": {
9 | "@schematics/angular:component": {
10 | "style": "scss"
11 | }
12 | },
13 | "root": "",
14 | "sourceRoot": "src",
15 | "prefix": "app",
16 | "architect": {
17 | "build": {
18 | "builder": "@angular-devkit/build-angular:browser",
19 | "options": {
20 | "outputPath": "dist/presentation",
21 | "index": "src/index.html",
22 | "main": "src/main.ts",
23 | "polyfills": "src/polyfills.ts",
24 | "tsConfig": "tsconfig.app.json",
25 | "inlineStyleLanguage": "scss",
26 | "assets": [
27 | "src/favicon.ico",
28 | "src/assets"
29 | ],
30 | "styles": [
31 | "src/styles.scss"
32 | ],
33 | "scripts": []
34 | },
35 | "configurations": {
36 | "production": {
37 | "budgets": [
38 | {
39 | "type": "initial",
40 | "maximumWarning": "500kb",
41 | "maximumError": "1mb"
42 | },
43 | {
44 | "type": "anyComponentStyle",
45 | "maximumWarning": "2kb",
46 | "maximumError": "4kb"
47 | }
48 | ],
49 | "fileReplacements": [
50 | {
51 | "replace": "src/environments/environment.ts",
52 | "with": "src/environments/environment.prod.ts"
53 | }
54 | ],
55 | "outputHashing": "all"
56 | },
57 | "development": {
58 | "buildOptimizer": false,
59 | "optimization": false,
60 | "vendorChunk": true,
61 | "extractLicenses": false,
62 | "sourceMap": true,
63 | "namedChunks": true
64 | }
65 | },
66 | "defaultConfiguration": "production"
67 | },
68 | "serve": {
69 | "builder": "@angular-devkit/build-angular:dev-server",
70 | "configurations": {
71 | "production": {
72 | "browserTarget": "presentation:build:production"
73 | },
74 | "development": {
75 | "browserTarget": "presentation:build:development"
76 | }
77 | },
78 | "defaultConfiguration": "development"
79 | },
80 | "extract-i18n": {
81 | "builder": "@angular-devkit/build-angular:extract-i18n",
82 | "options": {
83 | "browserTarget": "presentation:build"
84 | }
85 | },
86 | "test": {
87 | "builder": "@angular-devkit/build-angular:karma",
88 | "options": {
89 | "main": "src/test.ts",
90 | "polyfills": "src/polyfills.ts",
91 | "tsConfig": "tsconfig.spec.json",
92 | "karmaConfig": "karma.conf.js",
93 | "inlineStyleLanguage": "scss",
94 | "assets": [
95 | "src/favicon.ico",
96 | "src/assets"
97 | ],
98 | "styles": [
99 | "src/styles.scss"
100 | ],
101 | "scripts": []
102 | }
103 | }
104 | }
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # typescript-clean-architecture
2 |
3 | A typescript project template using clean architecture.
4 |
5 | You can use this template for any application you need, not just Web!
6 |
7 | # About
8 |
9 | ## Setting Up
10 |
11 | 1. Clone Repo
12 | 2. Within **root**, run `npm start`. This will install dependencies and setup lerna.
13 | 3. Done
14 |
15 | ## How to use
16 |
17 | You can read [this article](https://medium.com/@aziznal/27c7eb745ab4) explaining my idea of how this article can be used.
18 |
19 | Also, see the [counter-example](https://github.com/aziznal/typescript-clean-architecture/tree/counter-example) branch to see different layers in action together.
20 |
21 | The project structure is split into three main layers (core, data, presentation), as well as one auxiliary layer (di).
22 |
23 | A typical programming workflow for a new feature would look like this:
24 |
25 | - You start writing your app in **core**
26 | - You support **core** with a repository implemented in **data** that implements methods which use cases in **core** need
27 | - You create / update factories in **di** that create the **core** use cases / repos with their dependencies from **data** given to them
28 | - You setup the use of **core** use cases in **presentation**
29 | - You implement your feature in **presentation**
30 |
31 | Remember that **Presentation** knows ONLY Use Cases and Entities, NOT Repos! That's **Data**'s job, and it's hidden behind dependency injection.
32 |
33 | ## Useful commands to know
34 |
35 | - For each package (core, data, and di), you might want to run `npm run build:watch` while you're modifying that package. This'll make it
36 | rebuild for every save you make and other packages will be able to see those changes
37 | - Within root, `npx lerna run build` will re-build all your packages (that have changed since last build, otherwise it'll use cached version)
38 | - Within root, `npx lerna run build --scope=core` will re-build only core. You can substitute core for data, di, or presentation.
39 | - Within root, `npx lerna clean` will remove all node_modules folders in all packages including root.
40 |
41 | ## Extra
42 |
43 | VS Code workspaces are a life saver. Open each package in its own workspace and you'll have zero tsconfig or prettier issues etc.
44 |
45 | Sometimes, vs code (if that's what you're using) intellisense will not detect changes you made in another package. This is annoying as heck but closing
46 | and reopening vs code usually solves it. Sometimes, it just goes away on its own when a build command is ran.
47 |
48 | # Layers of the project
49 |
50 | ## Core
51 |
52 | Core is the layer where you can create your Entities, Use Cases, and Repo Interfaces.
53 |
54 | - **An Entity** is a model of an object in your application. It can be a User, a Course, or a Fruit. It's a Thing in your app that is core to how your app functions.
55 |
56 | - **A Use Case** is a way for you to use Entities to get something done. For example, a use case for a user would be login, sign up, add to favorites, send message to other user, etc..
57 |
58 | Use Cases typically require a data source to call on so they can retrieve data as well as save it. We cover this using a Repository interface defined in **core** and its implementation can then be defined in another layer such as **data**.
59 |
60 | - **Repository Interfaces** are what use cases call upon when they need their data. They define a few methods (such as create, get, update, delete, etc.. but it doesn't have to look like a REST api) and use cases call on these methods as they need them.
61 |
62 | ## Data
63 |
64 | Data is the layer that supports core with data. Use cases in core, through a repo interface, will call the data layer to request some data. The data layer manages whether that data is from an API or a cache, etc.
65 |
66 | ## DI (dependency injection)
67 | This is the layer where **Core** and **Data** are associated together and Data implementations are provided to instantiations of Core objects as they are needed. Typically, you'll be instantiating **Core** use case implementations and providing them with an implementation of a repository from **Data**.
68 |
69 | Example:
70 | ```typescript
71 | /* in di/src/core.factory.ts */
72 | import * as core from 'core';
73 | import * as data from 'data';
74 |
75 | class CoreFactory {
76 | userRepo: core.UserRepo;
77 |
78 | constructor(private otherDependencies: any[]) {
79 | this.userRepo = new data.UserRepoImpl(/* pass any required args here */);
80 | }
81 |
82 | createLoginUsecase(): core.LoginUsecase {
83 | return new core.LoginUsecaseImpl(this.userRepo)
84 | }
85 |
86 | createSignupUsecase(): core.SignupUsecase {
87 | return new core.LoginUsecaseImpl(this.userRepo)
88 | }
89 | }
90 | ```
91 |
92 | You can use this core factory in your presentation's dependency injection solution like this (Angular example provided):
93 |
94 | ```typescript
95 | /* in app.module.ts */
96 | import * as di from 'di';
97 |
98 | const coreFactory = new di.CoreFactory();
99 |
100 | @NgModule({
101 | // ...
102 | providers: [
103 | {
104 | provide: core.LoginUsecase,
105 | useFactory: () => coreFactory.createLoginUsecase()
106 | },
107 | {
108 | provide: core.SignupUsecase,
109 | useFactory: () => coreFactory.createSignupUsecase()
110 | },
111 | ],
112 | //...
113 | })
114 | export class AppModule {}
115 |
116 | ```
117 |
118 | Now you have access to your use cases through dependency injection!
119 |
120 | Note that the di layer and something like you app.module.ts file can become rather messy. This is normal. It's to be expected since they are the single point where everything is introduced together. There are a few things to do to make them tidier, such as isolating your providers array in app.module into its own file where you then import it into app.module. See [this example](https://github.com/aziznal/typescript-clean-architecture/blob/counter-example/packages/presentation/src/di/counter.ioc.ts) of a providers file and how it's used in the [app.module](https://github.com/aziznal/typescript-clean-architecture/blob/counter-example/packages/presentation/src/app/app.module.ts) file. It doesn't get easier!
121 |
122 | ## Presentation
123 | This is the layer where you write you UI code. You can do that using a web framework such as Angular or React, or using something else like Electron if you're making a desktop app. Heck, you can even make a console app if you wanted!
124 |
125 | ## Other Layers
126 | You can very easily add more layers to this architecture, depending on your need. For example, maybe you prefer to isolate caching into its own layer because you have a complicated caching mechanism, or perhaps you have multiple implementations of presentation such as one for web and one for desktop etc.
127 |
--------------------------------------------------------------------------------