@mytechtoday/augment-extensions 1.7.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -8
- package/augment-extensions/coding-standards/bash/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/bash/metadata.json +21 -0
- package/augment-extensions/coding-standards/c/metadata.json +50 -0
- package/augment-extensions/coding-standards/css/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/css/metadata.json +30 -0
- package/augment-extensions/coding-standards/go/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/go/metadata.json +57 -0
- package/augment-extensions/coding-standards/html/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/html/metadata.json +25 -0
- package/augment-extensions/coding-standards/html-css-js/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/html-css-js/metadata.json +40 -0
- package/augment-extensions/coding-standards/js/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/js/metadata.json +37 -0
- package/augment-extensions/coding-standards/php/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/php/metadata.json +35 -0
- package/augment-extensions/coding-standards/powershell/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/powershell/metadata.json +33 -0
- package/augment-extensions/coding-standards/python/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/python/metadata.json +38 -0
- package/augment-extensions/coding-standards/react/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/react/metadata.json +31 -0
- package/augment-extensions/coding-standards/typescript/CHANGELOG.md +39 -0
- package/augment-extensions/coding-standards/typescript/README.md +248 -19
- package/augment-extensions/coding-standards/typescript/VERSION +2 -0
- package/augment-extensions/coding-standards/typescript/examples/biome.json +199 -0
- package/augment-extensions/coding-standards/typescript/examples/eslint-flat-config.js +166 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/README.md +47 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/package.json +37 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/pnpm-workspace.yaml +5 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/tsconfig.base.json +50 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/turbo.json +47 -0
- package/augment-extensions/coding-standards/typescript/examples/vitest-setup.ts +198 -0
- package/augment-extensions/coding-standards/typescript/metadata.json +46 -0
- package/augment-extensions/coding-standards/typescript/module.json +25 -7
- package/augment-extensions/coding-standards/typescript/rules/architecture.md +1096 -0
- package/augment-extensions/coding-standards/typescript/rules/error-handling.md +1174 -0
- package/augment-extensions/coding-standards/typescript/rules/modern-features.md +625 -0
- package/augment-extensions/coding-standards/typescript/rules/monorepo.md +745 -0
- package/augment-extensions/coding-standards/typescript/rules/security-performance.md +850 -0
- package/augment-extensions/coding-standards/typescript/rules/testing.md +918 -0
- package/augment-extensions/coding-standards/typescript/rules/tooling.md +1071 -0
- package/augment-extensions/coding-standards/typescript/rules/type-patterns.md +750 -0
- package/augment-extensions/domain-rules/api-design/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/api-design/metadata.json +32 -0
- package/augment-extensions/domain-rules/database/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/database/metadata.json +50 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-latte/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-latte/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-mocha/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-mocha/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/dracula/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/dracula/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-dark/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-dark/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-light/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-light/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/high-contrast/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/high-contrast/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/monokai/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/monokai/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/nord/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/nord/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/one-dark/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/one-dark/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/one-light/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/one-light/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-dark/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-dark/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-light/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-light/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/tokyo-night/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/tokyo-night/metadata.json +18 -0
- package/augment-extensions/domain-rules/mcp/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/mcp/metadata.json +42 -0
- package/augment-extensions/domain-rules/security/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/security/metadata.json +33 -0
- package/augment-extensions/domain-rules/seo-sales-marketing/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/seo-sales-marketing/metadata.json +40 -0
- package/augment-extensions/domain-rules/software-architecture/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/software-architecture/metadata.json +55 -0
- package/augment-extensions/domain-rules/wordpress/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/wordpress/metadata.json +42 -0
- package/augment-extensions/domain-rules/wordpress-plugin/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/wordpress-plugin/metadata.json +70 -0
- package/augment-extensions/examples/design-patterns/CHANGELOG.md +51 -0
- package/augment-extensions/examples/design-patterns/metadata.json +29 -0
- package/augment-extensions/examples/gutenberg-block-plugin/CHANGELOG.md +51 -0
- package/augment-extensions/examples/gutenberg-block-plugin/metadata.json +26 -0
- package/augment-extensions/examples/rest-api-plugin/CHANGELOG.md +51 -0
- package/augment-extensions/examples/rest-api-plugin/metadata.json +27 -0
- package/augment-extensions/examples/woocommerce-extension/CHANGELOG.md +51 -0
- package/augment-extensions/examples/woocommerce-extension/metadata.json +27 -0
- package/augment-extensions/visual-design/metadata.json +34 -0
- package/augment-extensions/workflows/adr-support/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/adr-support/metadata.json +41 -0
- package/augment-extensions/workflows/beads/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/beads/metadata.json +23 -0
- package/augment-extensions/workflows/beads-integration/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/beads-integration/metadata.json +30 -0
- package/augment-extensions/workflows/database/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/database/metadata.json +30 -0
- package/augment-extensions/workflows/wordpress-plugin/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/wordpress-plugin/metadata.json +41 -0
- package/augment-extensions/writing-standards/literature/shakespeare/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/literature/shakespeare/metadata.json +60 -0
- package/augment-extensions/writing-standards/screenplay/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/monty-python/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/monty-python/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/saturday-night-live/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/saturday-night-live/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/alfred-hitchcock/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/alfred-hitchcock/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/ari-aster/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/ari-aster/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/brad-bird/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/brad-bird/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/brian-de-palma/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/brian-de-palma/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/buster-keaton/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/buster-keaton/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/christopher-nolan/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/christopher-nolan/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/clint-eastwood/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/clint-eastwood/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/coen-brothers/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/coen-brothers/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/darren-aronofsky/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/darren-aronofsky/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/david-fincher/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/david-fincher/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/david-lynch/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/david-lynch/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/denis-villeneuve/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/denis-villeneuve/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/francis-ford-coppola/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/francis-ford-coppola/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/gary-marshall/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/gary-marshall/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/george-a-romero/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/george-a-romero/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/george-lucas/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/george-lucas/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/guillermo-del-toro/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/guillermo-del-toro/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/gus-van-sant/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/gus-van-sant/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/james-ivory-ismail-merchant/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/james-ivory-ismail-merchant/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/jim-jarmusch/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/jim-jarmusch/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-carpenter/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-carpenter/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-ford/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-ford/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-huston/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-huston/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-landis/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-landis/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/jonathan-demme/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/jonathan-demme/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/joseph-l-mankiewicz/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/joseph-l-mankiewicz/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kathryn-bigelow/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kathryn-bigelow/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kelly-reichardt/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kelly-reichardt/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kevin-smith/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kevin-smith/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/linda-shayne/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/linda-shayne/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/martin-scorsese/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/martin-scorsese/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/mel-brooks/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/mel-brooks/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/metadata.json +25 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/michael-curtiz/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/michael-curtiz/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/michael-mann/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/michael-mann/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/mike-nichols/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/mike-nichols/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/orson-welles/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/orson-welles/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/park-chan-wook/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/park-chan-wook/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/paul-thomas-anderson/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/paul-thomas-anderson/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/penny-marshall/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/penny-marshall/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/peter-bogdanovich/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/peter-bogdanovich/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/quentin-tarantino/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/quentin-tarantino/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/richard-linklater/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/richard-linklater/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/rob-reiner/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/rob-reiner/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-altman/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-altman/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-eggers/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-eggers/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-zemeckis/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-zemeckis/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sam-peckinpah/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sam-peckinpah/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sidney-lumet/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sidney-lumet/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/spike-lee/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/spike-lee/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/stanley-donen-gene-kelly/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/stanley-donen-gene-kelly/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/stanley-kubrick/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/stanley-kubrick/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/steve-martin/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/steve-martin/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/steven-spielberg/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/steven-spielberg/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sydney-pollack/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sydney-pollack/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/terry-gilliam/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/terry-gilliam/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/tim-burton/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/tim-burton/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/tobe-hooper/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/tobe-hooper/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/wes-anderson/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/wes-anderson/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/william-friedkin/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/william-friedkin/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/films/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/films/blue-ruin/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/films/blue-ruin/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/films/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/fast-and-furious/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/fast-and-furious/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/harry-potter/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/harry-potter/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/james-bond/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/james-bond/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/john-wick/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/john-wick/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/lord-of-the-rings/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/lord-of-the-rings/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/mcu/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/mcu/metadata.json +25 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/metadata.json +25 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/star-trek/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/star-trek/metadata.json +33 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/star-wars/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/star-wars/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/metadata.json +50 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/narrative-theory/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/narrative-theory/joseph-campbell/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/narrative-theory/joseph-campbell/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/narrative-theory/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/producers/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/producers/bruckheimer-and-simpson/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/producers/bruckheimer-and-simpson/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/producers/metadata.json +23 -0
- package/augment-extensions/writing-standards/screenplay/genres/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/genres/metadata.json +43 -0
- package/augment-extensions/writing-standards/screenplay/metadata.json +65 -0
- package/augment-extensions/writing-standards/screenplay/styles/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/styles/metadata.json +38 -0
- package/augment-extensions/writing-standards/screenplay/themes/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/themes/metadata.json +47 -0
- package/cli/dist/__test_health-checker.d.ts +2 -0
- package/cli/dist/__test_health-checker.d.ts.map +1 -0
- package/cli/dist/__test_health-checker.js +4 -0
- package/cli/dist/__test_health-checker.js.map +1 -0
- package/cli/dist/__test_template-engine.d.ts +2 -0
- package/cli/dist/__test_template-engine.d.ts.map +1 -0
- package/cli/dist/__test_template-engine.js +4 -0
- package/cli/dist/__test_template-engine.js.map +1 -0
- package/cli/dist/cli.js +25 -0
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/index.d.ts +8 -2
- package/cli/dist/commands/generate-shot-list/generator/index.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/index.js +28 -3
- package/cli/dist/commands/generate-shot-list/generator/index.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/types.d.ts +3 -0
- package/cli/dist/commands/generate-shot-list/generator/types.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/help-text.d.ts +1 -1
- package/cli/dist/commands/generate-shot-list/help-text.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/help-text.js +48 -5
- package/cli/dist/commands/generate-shot-list/help-text.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/logger/jsonl-writer.d.ts +11 -1
- package/cli/dist/commands/generate-shot-list/logger/jsonl-writer.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/logger/jsonl-writer.js +61 -1
- package/cli/dist/commands/generate-shot-list/logger/jsonl-writer.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/parser/docx-parser.d.ts +29 -0
- package/cli/dist/commands/generate-shot-list/parser/docx-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/docx-parser.js +111 -0
- package/cli/dist/commands/generate-shot-list/parser/docx-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/finaldraft-parser.d.ts +18 -0
- package/cli/dist/commands/generate-shot-list/parser/finaldraft-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/finaldraft-parser.js +184 -0
- package/cli/dist/commands/generate-shot-list/parser/finaldraft-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/index.d.ts +4 -3
- package/cli/dist/commands/generate-shot-list/parser/index.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/parser/index.js +54 -3
- package/cli/dist/commands/generate-shot-list/parser/index.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/parser/pdf-parser.d.ts +24 -0
- package/cli/dist/commands/generate-shot-list/parser/pdf-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/pdf-parser.js +84 -0
- package/cli/dist/commands/generate-shot-list/parser/pdf-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/rtf-parser.d.ts +28 -0
- package/cli/dist/commands/generate-shot-list/parser/rtf-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/rtf-parser.js +143 -0
- package/cli/dist/commands/generate-shot-list/parser/rtf-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/types.d.ts +9 -6
- package/cli/dist/commands/generate-shot-list/parser/types.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/parser/types.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/style/guideline-parser.d.ts +36 -0
- package/cli/dist/commands/generate-shot-list/style/guideline-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/guideline-parser.js +108 -0
- package/cli/dist/commands/generate-shot-list/style/guideline-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/index.d.ts +39 -0
- package/cli/dist/commands/generate-shot-list/style/index.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/index.js +85 -0
- package/cli/dist/commands/generate-shot-list/style/index.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/style-loader.d.ts +35 -0
- package/cli/dist/commands/generate-shot-list/style/style-loader.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/style-loader.js +178 -0
- package/cli/dist/commands/generate-shot-list/style/style-loader.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/style-merger.d.ts +36 -0
- package/cli/dist/commands/generate-shot-list/style/style-merger.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/style-merger.js +123 -0
- package/cli/dist/commands/generate-shot-list/style/style-merger.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/types.d.ts +105 -0
- package/cli/dist/commands/generate-shot-list/style/types.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/types.js +8 -0
- package/cli/dist/commands/generate-shot-list/style/types.js.map +1 -0
- package/cli/dist/commands/generate-shot-list.d.ts +1 -0
- package/cli/dist/commands/generate-shot-list.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list.js +44 -1
- package/cli/dist/commands/generate-shot-list.js.map +1 -1
- package/cli/dist/commands/list.d.ts +1 -0
- package/cli/dist/commands/list.d.ts.map +1 -1
- package/cli/dist/commands/list.js +21 -5
- package/cli/dist/commands/list.js.map +1 -1
- package/cli/dist/commands/upgrade.d.ts +15 -0
- package/cli/dist/commands/upgrade.d.ts.map +1 -0
- package/cli/dist/commands/upgrade.js +229 -0
- package/cli/dist/commands/upgrade.js.map +1 -0
- package/cli/dist/commands/use.d.ts +15 -0
- package/cli/dist/commands/use.d.ts.map +1 -0
- package/cli/dist/commands/use.js +180 -0
- package/cli/dist/commands/use.js.map +1 -0
- package/cli/dist/commands/version-info.d.ts +15 -0
- package/cli/dist/commands/version-info.d.ts.map +1 -0
- package/cli/dist/commands/version-info.js +194 -0
- package/cli/dist/commands/version-info.js.map +1 -0
- package/cli/dist/core/compatibility-checker.d.ts +73 -0
- package/cli/dist/core/compatibility-checker.d.ts.map +1 -0
- package/cli/dist/core/compatibility-checker.js +188 -0
- package/cli/dist/core/compatibility-checker.js.map +1 -0
- package/cli/dist/core/index.d.ts +9 -0
- package/cli/dist/core/index.d.ts.map +1 -0
- package/cli/dist/core/index.js +16 -0
- package/cli/dist/core/index.js.map +1 -0
- package/cli/dist/core/module-loader.d.ts +68 -0
- package/cli/dist/core/module-loader.d.ts.map +1 -0
- package/cli/dist/core/module-loader.js +137 -0
- package/cli/dist/core/module-loader.js.map +1 -0
- package/cli/dist/core/version-manager.d.ts +62 -0
- package/cli/dist/core/version-manager.d.ts.map +1 -0
- package/cli/dist/core/version-manager.js +157 -0
- package/cli/dist/core/version-manager.js.map +1 -0
- package/cli/dist/core/version-resolver.d.ts +63 -0
- package/cli/dist/core/version-resolver.d.ts.map +1 -0
- package/cli/dist/core/version-resolver.js +163 -0
- package/cli/dist/core/version-resolver.js.map +1 -0
- package/cli/dist/gui/app.d.ts +17 -0
- package/cli/dist/gui/app.d.ts.map +1 -0
- package/cli/dist/gui/app.js +79 -0
- package/cli/dist/gui/app.js.map +1 -0
- package/cli/dist/gui/components/preview-pane.d.ts +40 -0
- package/cli/dist/gui/components/preview-pane.d.ts.map +1 -0
- package/cli/dist/gui/components/preview-pane.js +90 -0
- package/cli/dist/gui/components/preview-pane.js.map +1 -0
- package/cli/dist/gui/components/search-filter.d.ts +26 -0
- package/cli/dist/gui/components/search-filter.d.ts.map +1 -0
- package/cli/dist/gui/components/search-filter.js +168 -0
- package/cli/dist/gui/components/search-filter.js.map +1 -0
- package/cli/dist/gui/components/status-bar.d.ts +22 -0
- package/cli/dist/gui/components/status-bar.d.ts.map +1 -0
- package/cli/dist/gui/components/status-bar.js +58 -0
- package/cli/dist/gui/components/status-bar.js.map +1 -0
- package/cli/dist/gui/components/tree-navigator.d.ts +30 -0
- package/cli/dist/gui/components/tree-navigator.d.ts.map +1 -0
- package/cli/dist/gui/components/tree-navigator.js +133 -0
- package/cli/dist/gui/components/tree-navigator.js.map +1 -0
- package/cli/dist/gui/components/version-selector.d.ts +27 -0
- package/cli/dist/gui/components/version-selector.d.ts.map +1 -0
- package/cli/dist/gui/components/version-selector.js +110 -0
- package/cli/dist/gui/components/version-selector.js.map +1 -0
- package/cli/dist/gui/hooks/use-version-loader.d.ts +25 -0
- package/cli/dist/gui/hooks/use-version-loader.d.ts.map +1 -0
- package/cli/dist/gui/hooks/use-version-loader.js +116 -0
- package/cli/dist/gui/hooks/use-version-loader.js.map +1 -0
- package/cli/dist/gui/index.d.ts +15 -0
- package/cli/dist/gui/index.d.ts.map +1 -0
- package/cli/dist/gui/index.js +39 -0
- package/cli/dist/gui/index.js.map +1 -0
- package/cli/dist/gui/layouts/main-layout.d.ts +14 -0
- package/cli/dist/gui/layouts/main-layout.d.ts.map +1 -0
- package/cli/dist/gui/layouts/main-layout.js +205 -0
- package/cli/dist/gui/layouts/main-layout.js.map +1 -0
- package/cli/dist/gui/state/navigation-state.d.ts +39 -0
- package/cli/dist/gui/state/navigation-state.d.ts.map +1 -0
- package/cli/dist/gui/state/navigation-state.js +151 -0
- package/cli/dist/gui/state/navigation-state.js.map +1 -0
- package/cli/dist/gui/state/selection-state.d.ts +36 -0
- package/cli/dist/gui/state/selection-state.d.ts.map +1 -0
- package/cli/dist/gui/state/selection-state.js +132 -0
- package/cli/dist/gui/state/selection-state.js.map +1 -0
- package/cli/dist/gui/theme.d.ts +50 -0
- package/cli/dist/gui/theme.d.ts.map +1 -0
- package/cli/dist/gui/theme.js +73 -0
- package/cli/dist/gui/theme.js.map +1 -0
- package/cli/dist/utils/help.d.ts +13 -0
- package/cli/dist/utils/help.d.ts.map +1 -0
- package/cli/dist/utils/help.js +137 -0
- package/cli/dist/utils/help.js.map +1 -0
- package/cli/dist/utils/module-system.d.ts +8 -0
- package/cli/dist/utils/module-system.d.ts.map +1 -1
- package/cli/dist/utils/module-system.js +38 -3
- package/cli/dist/utils/module-system.js.map +1 -1
- package/cli/dist/utils/validate-versioning.d.ts +43 -0
- package/cli/dist/utils/validate-versioning.d.ts.map +1 -0
- package/cli/dist/utils/validate-versioning.js +258 -0
- package/cli/dist/utils/validate-versioning.js.map +1 -0
- package/package.json +10 -1
|
@@ -0,0 +1,1096 @@
|
|
|
1
|
+
# TypeScript Architecture Patterns
|
|
2
|
+
|
|
3
|
+
Comprehensive guide to architectural patterns for organizing TypeScript applications.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
- [Folder Organization](#folder-organization)
|
|
7
|
+
- [Dependency Inversion](#dependency-inversion)
|
|
8
|
+
- [Domain-Driven Design Basics](#domain-driven-design-basics)
|
|
9
|
+
- [Layered Architecture](#layered-architecture)
|
|
10
|
+
- [Clean Architecture](#clean-architecture)
|
|
11
|
+
- [Hexagonal Architecture](#hexagonal-architecture)
|
|
12
|
+
- [Best Practices](#best-practices)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Folder Organization
|
|
17
|
+
|
|
18
|
+
### Folder-by-Convention (Traditional)
|
|
19
|
+
|
|
20
|
+
Organize by technical role (controllers, services, models).
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
src/
|
|
24
|
+
├── controllers/
|
|
25
|
+
│ ├── user-controller.ts
|
|
26
|
+
│ ├── product-controller.ts
|
|
27
|
+
│ └── order-controller.ts
|
|
28
|
+
├── services/
|
|
29
|
+
│ ├── user-service.ts
|
|
30
|
+
│ ├── product-service.ts
|
|
31
|
+
│ └── order-service.ts
|
|
32
|
+
├── models/
|
|
33
|
+
│ ├── user.ts
|
|
34
|
+
│ ├── product.ts
|
|
35
|
+
│ └── order.ts
|
|
36
|
+
├── repositories/
|
|
37
|
+
│ ├── user-repository.ts
|
|
38
|
+
│ ├── product-repository.ts
|
|
39
|
+
│ └── order-repository.ts
|
|
40
|
+
└── utils/
|
|
41
|
+
└── helpers.ts
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Pros:**
|
|
45
|
+
- Familiar structure
|
|
46
|
+
- Easy to understand for small projects
|
|
47
|
+
- Clear separation of technical concerns
|
|
48
|
+
|
|
49
|
+
**Cons:**
|
|
50
|
+
- Related code is scattered
|
|
51
|
+
- Hard to find all code for a feature
|
|
52
|
+
- Difficult to scale
|
|
53
|
+
- Tight coupling between layers
|
|
54
|
+
|
|
55
|
+
### Folder-by-Feature (Recommended)
|
|
56
|
+
|
|
57
|
+
Organize by business domain/feature.
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
src/
|
|
61
|
+
├── users/
|
|
62
|
+
│ ├── user.entity.ts
|
|
63
|
+
│ ├── user.repository.ts
|
|
64
|
+
│ ├── user.service.ts
|
|
65
|
+
│ ├── user.controller.ts
|
|
66
|
+
│ ├── dto/
|
|
67
|
+
│ │ ├── create-user.dto.ts
|
|
68
|
+
│ │ └── update-user.dto.ts
|
|
69
|
+
│ ├── __tests__/
|
|
70
|
+
│ │ ├── user.service.test.ts
|
|
71
|
+
│ │ └── user.controller.test.ts
|
|
72
|
+
│ └── index.ts
|
|
73
|
+
├── products/
|
|
74
|
+
│ ├── product.entity.ts
|
|
75
|
+
│ ├── product.repository.ts
|
|
76
|
+
│ ├── product.service.ts
|
|
77
|
+
│ ├── product.controller.ts
|
|
78
|
+
│ └── index.ts
|
|
79
|
+
├── orders/
|
|
80
|
+
│ ├── order.entity.ts
|
|
81
|
+
│ ├── order.repository.ts
|
|
82
|
+
│ ├── order.service.ts
|
|
83
|
+
│ ├── order.controller.ts
|
|
84
|
+
│ └── index.ts
|
|
85
|
+
└── shared/
|
|
86
|
+
├── database/
|
|
87
|
+
├── utils/
|
|
88
|
+
└── types/
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Pros:**
|
|
92
|
+
- All related code in one place
|
|
93
|
+
- Easy to find feature code
|
|
94
|
+
- Better encapsulation
|
|
95
|
+
- Easier to scale
|
|
96
|
+
- Can delete entire feature folder
|
|
97
|
+
|
|
98
|
+
**Cons:**
|
|
99
|
+
- Less familiar initially
|
|
100
|
+
- Shared code needs careful management
|
|
101
|
+
|
|
102
|
+
### Hybrid Approach
|
|
103
|
+
|
|
104
|
+
Combine both approaches for best results.
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
src/
|
|
108
|
+
├── features/ # Feature modules
|
|
109
|
+
│ ├── users/
|
|
110
|
+
│ │ ├── domain/ # Business logic
|
|
111
|
+
│ │ │ ├── user.entity.ts
|
|
112
|
+
│ │ │ └── user.service.ts
|
|
113
|
+
│ │ ├── infrastructure/ # External concerns
|
|
114
|
+
│ │ │ └── user.repository.ts
|
|
115
|
+
│ │ ├── presentation/ # API/UI layer
|
|
116
|
+
│ │ │ └── user.controller.ts
|
|
117
|
+
│ │ └── index.ts
|
|
118
|
+
│ ├── products/
|
|
119
|
+
│ └── orders/
|
|
120
|
+
├── shared/ # Shared across features
|
|
121
|
+
│ ├── domain/
|
|
122
|
+
│ │ ├── base-entity.ts
|
|
123
|
+
│ │ └── value-objects/
|
|
124
|
+
│ ├── infrastructure/
|
|
125
|
+
│ │ ├── database/
|
|
126
|
+
│ │ └── cache/
|
|
127
|
+
│ └── utils/
|
|
128
|
+
└── core/ # Core application
|
|
129
|
+
├── config/
|
|
130
|
+
├── middleware/
|
|
131
|
+
└── types/
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Dependency Inversion
|
|
137
|
+
|
|
138
|
+
The Dependency Inversion Principle (DIP): High-level modules should not depend on low-level modules. Both should depend on abstractions.
|
|
139
|
+
|
|
140
|
+
### Without Dependency Inversion (Bad)
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// ❌ Bad - Direct dependency on concrete implementation
|
|
144
|
+
class UserService {
|
|
145
|
+
private repository = new MySQLUserRepository(); // Tight coupling
|
|
146
|
+
|
|
147
|
+
async getUser(id: string): Promise<User> {
|
|
148
|
+
return this.repository.findById(id);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### With Dependency Inversion (Good)
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// ✅ Good - Depend on abstraction
|
|
157
|
+
interface UserRepository {
|
|
158
|
+
findById(id: string): Promise<User | null>;
|
|
159
|
+
save(user: User): Promise<User>;
|
|
160
|
+
delete(id: string): Promise<void>;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
class UserService {
|
|
164
|
+
constructor(private repository: UserRepository) {} // Inject dependency
|
|
165
|
+
|
|
166
|
+
async getUser(id: string): Promise<User> {
|
|
167
|
+
const user = await this.repository.findById(id);
|
|
168
|
+
if (!user) throw new Error('User not found');
|
|
169
|
+
return user;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Implementations
|
|
174
|
+
class MySQLUserRepository implements UserRepository {
|
|
175
|
+
async findById(id: string): Promise<User | null> {
|
|
176
|
+
// MySQL implementation
|
|
177
|
+
}
|
|
178
|
+
// ... other methods
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
class MongoUserRepository implements UserRepository {
|
|
182
|
+
async findById(id: string): Promise<User | null> {
|
|
183
|
+
// MongoDB implementation
|
|
184
|
+
}
|
|
185
|
+
// ... other methods
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Usage - Easy to swap implementations
|
|
189
|
+
const service = new UserService(new MySQLUserRepository());
|
|
190
|
+
// or
|
|
191
|
+
const service = new UserService(new MongoUserRepository());
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Dependency Injection Container
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// container.ts
|
|
198
|
+
import { Container } from 'inversify';
|
|
199
|
+
import 'reflect-metadata';
|
|
200
|
+
|
|
201
|
+
const container = new Container();
|
|
202
|
+
|
|
203
|
+
// Bind interfaces to implementations
|
|
204
|
+
container.bind<UserRepository>('UserRepository').to(MySQLUserRepository);
|
|
205
|
+
container.bind<UserService>('UserService').to(UserService);
|
|
206
|
+
|
|
207
|
+
export { container };
|
|
208
|
+
|
|
209
|
+
// user.service.ts
|
|
210
|
+
import { injectable, inject } from 'inversify';
|
|
211
|
+
|
|
212
|
+
@injectable()
|
|
213
|
+
class UserService {
|
|
214
|
+
constructor(
|
|
215
|
+
@inject('UserRepository') private repository: UserRepository
|
|
216
|
+
) {}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Domain-Driven Design Basics
|
|
223
|
+
|
|
224
|
+
DDD focuses on modeling the business domain.
|
|
225
|
+
|
|
226
|
+
### Entities
|
|
227
|
+
|
|
228
|
+
Objects with identity that persist over time.
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
// user.entity.ts
|
|
232
|
+
export class User {
|
|
233
|
+
constructor(
|
|
234
|
+
public readonly id: string,
|
|
235
|
+
private _email: string,
|
|
236
|
+
private _name: string,
|
|
237
|
+
private _createdAt: Date = new Date()
|
|
238
|
+
) {}
|
|
239
|
+
|
|
240
|
+
get email(): string {
|
|
241
|
+
return this._email;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
get name(): string {
|
|
245
|
+
return this._name;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
updateEmail(newEmail: string): void {
|
|
249
|
+
if (!this.isValidEmail(newEmail)) {
|
|
250
|
+
throw new Error('Invalid email format');
|
|
251
|
+
}
|
|
252
|
+
this._email = newEmail;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private isValidEmail(email: string): boolean {
|
|
256
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Value Objects
|
|
262
|
+
|
|
263
|
+
Objects without identity, defined by their attributes.
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// email.value-object.ts
|
|
267
|
+
export class Email {
|
|
268
|
+
private constructor(private readonly value: string) {}
|
|
269
|
+
|
|
270
|
+
static create(email: string): Email {
|
|
271
|
+
if (!this.isValid(email)) {
|
|
272
|
+
throw new Error('Invalid email format');
|
|
273
|
+
}
|
|
274
|
+
return new Email(email.toLowerCase());
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private static isValid(email: string): boolean {
|
|
278
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
toString(): string {
|
|
282
|
+
return this.value;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
equals(other: Email): boolean {
|
|
286
|
+
return this.value === other.value;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// money.value-object.ts
|
|
291
|
+
export class Money {
|
|
292
|
+
private constructor(
|
|
293
|
+
private readonly amount: number,
|
|
294
|
+
private readonly currency: string
|
|
295
|
+
) {}
|
|
296
|
+
|
|
297
|
+
static create(amount: number, currency: string): Money {
|
|
298
|
+
if (amount < 0) throw new Error('Amount cannot be negative');
|
|
299
|
+
if (!['USD', 'EUR', 'GBP'].includes(currency)) {
|
|
300
|
+
throw new Error('Invalid currency');
|
|
301
|
+
}
|
|
302
|
+
return new Money(amount, currency);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
add(other: Money): Money {
|
|
306
|
+
if (this.currency !== other.currency) {
|
|
307
|
+
throw new Error('Cannot add different currencies');
|
|
308
|
+
}
|
|
309
|
+
return Money.create(this.amount + other.amount, this.currency);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
toString(): string {
|
|
313
|
+
return `${this.amount} ${this.currency}`;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Aggregates
|
|
319
|
+
|
|
320
|
+
Cluster of entities and value objects with a root entity.
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
// order.aggregate.ts
|
|
324
|
+
export class Order {
|
|
325
|
+
private _items: OrderItem[] = [];
|
|
326
|
+
private _status: OrderStatus = 'pending';
|
|
327
|
+
|
|
328
|
+
constructor(
|
|
329
|
+
public readonly id: string,
|
|
330
|
+
private _customerId: string
|
|
331
|
+
) {}
|
|
332
|
+
|
|
333
|
+
addItem(product: Product, quantity: number): void {
|
|
334
|
+
if (this._status !== 'pending') {
|
|
335
|
+
throw new Error('Cannot modify confirmed order');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const item = new OrderItem(product, quantity);
|
|
339
|
+
this._items.push(item);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
removeItem(productId: string): void {
|
|
343
|
+
if (this._status !== 'pending') {
|
|
344
|
+
throw new Error('Cannot modify confirmed order');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
this._items = this._items.filter(item => item.productId !== productId);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
confirm(): void {
|
|
351
|
+
if (this._items.length === 0) {
|
|
352
|
+
throw new Error('Cannot confirm empty order');
|
|
353
|
+
}
|
|
354
|
+
this._status = 'confirmed';
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
get total(): Money {
|
|
358
|
+
return this._items.reduce(
|
|
359
|
+
(sum, item) => sum.add(item.total),
|
|
360
|
+
Money.create(0, 'USD')
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
get items(): readonly OrderItem[] {
|
|
365
|
+
return [...this._items]; // Return copy to prevent external modification
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
class OrderItem {
|
|
370
|
+
constructor(
|
|
371
|
+
private _product: Product,
|
|
372
|
+
private _quantity: number
|
|
373
|
+
) {}
|
|
374
|
+
|
|
375
|
+
get productId(): string {
|
|
376
|
+
return this._product.id;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
get total(): Money {
|
|
380
|
+
return this._product.price.multiply(this._quantity);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Repositories (DDD Pattern)
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
// user.repository.interface.ts
|
|
389
|
+
export interface UserRepository {
|
|
390
|
+
findById(id: string): Promise<User | null>;
|
|
391
|
+
findByEmail(email: Email): Promise<User | null>;
|
|
392
|
+
save(user: User): Promise<void>;
|
|
393
|
+
delete(id: string): Promise<void>;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// user.repository.impl.ts
|
|
397
|
+
export class PrismaUserRepository implements UserRepository {
|
|
398
|
+
constructor(private prisma: PrismaClient) {}
|
|
399
|
+
|
|
400
|
+
async findById(id: string): Promise<User | null> {
|
|
401
|
+
const data = await this.prisma.user.findUnique({ where: { id } });
|
|
402
|
+
return data ? this.toDomain(data) : null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async save(user: User): Promise<void> {
|
|
406
|
+
await this.prisma.user.upsert({
|
|
407
|
+
where: { id: user.id },
|
|
408
|
+
create: this.toPersistence(user),
|
|
409
|
+
update: this.toPersistence(user)
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
private toDomain(data: any): User {
|
|
414
|
+
return new User(data.id, data.email, data.name, data.createdAt);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private toPersistence(user: User): any {
|
|
418
|
+
return {
|
|
419
|
+
id: user.id,
|
|
420
|
+
email: user.email,
|
|
421
|
+
name: user.name
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Domain Services
|
|
428
|
+
|
|
429
|
+
Business logic that doesn't belong to a single entity.
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
// transfer-money.service.ts
|
|
433
|
+
export class TransferMoneyService {
|
|
434
|
+
constructor(
|
|
435
|
+
private accountRepository: AccountRepository,
|
|
436
|
+
private transactionRepository: TransactionRepository
|
|
437
|
+
) {}
|
|
438
|
+
|
|
439
|
+
async transfer(
|
|
440
|
+
fromAccountId: string,
|
|
441
|
+
toAccountId: string,
|
|
442
|
+
amount: Money
|
|
443
|
+
): Promise<void> {
|
|
444
|
+
const fromAccount = await this.accountRepository.findById(fromAccountId);
|
|
445
|
+
const toAccount = await this.accountRepository.findById(toAccountId);
|
|
446
|
+
|
|
447
|
+
if (!fromAccount || !toAccount) {
|
|
448
|
+
throw new Error('Account not found');
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Business logic
|
|
452
|
+
fromAccount.withdraw(amount);
|
|
453
|
+
toAccount.deposit(amount);
|
|
454
|
+
|
|
455
|
+
// Persist changes
|
|
456
|
+
await this.accountRepository.save(fromAccount);
|
|
457
|
+
await this.accountRepository.save(toAccount);
|
|
458
|
+
|
|
459
|
+
// Record transaction
|
|
460
|
+
const transaction = new Transaction(fromAccountId, toAccountId, amount);
|
|
461
|
+
await this.transactionRepository.save(transaction);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## Layered Architecture
|
|
469
|
+
|
|
470
|
+
Organize code into horizontal layers.
|
|
471
|
+
|
|
472
|
+
```
|
|
473
|
+
┌─────────────────────────────────┐
|
|
474
|
+
│ Presentation Layer │ ← Controllers, Views, DTOs
|
|
475
|
+
├─────────────────────────────────┤
|
|
476
|
+
│ Application Layer │ ← Use Cases, Application Services
|
|
477
|
+
├─────────────────────────────────┤
|
|
478
|
+
│ Domain Layer │ ← Entities, Value Objects, Domain Services
|
|
479
|
+
├─────────────────────────────────┤
|
|
480
|
+
│ Infrastructure Layer │ ← Repositories, External Services, DB
|
|
481
|
+
└─────────────────────────────────┘
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Implementation
|
|
485
|
+
|
|
486
|
+
```typescript
|
|
487
|
+
// Presentation Layer
|
|
488
|
+
// user.controller.ts
|
|
489
|
+
export class UserController {
|
|
490
|
+
constructor(private createUserUseCase: CreateUserUseCase) {}
|
|
491
|
+
|
|
492
|
+
async create(req: Request, res: Response): Promise<void> {
|
|
493
|
+
const dto = CreateUserDto.from(req.body);
|
|
494
|
+
const user = await this.createUserUseCase.execute(dto);
|
|
495
|
+
res.status(201).json(user);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Application Layer
|
|
500
|
+
// create-user.use-case.ts
|
|
501
|
+
export class CreateUserUseCase {
|
|
502
|
+
constructor(
|
|
503
|
+
private userRepository: UserRepository,
|
|
504
|
+
private emailService: EmailService
|
|
505
|
+
) {}
|
|
506
|
+
|
|
507
|
+
async execute(dto: CreateUserDto): Promise<User> {
|
|
508
|
+
const email = Email.create(dto.email);
|
|
509
|
+
const user = User.create(dto.name, email);
|
|
510
|
+
|
|
511
|
+
await this.userRepository.save(user);
|
|
512
|
+
await this.emailService.sendWelcomeEmail(user);
|
|
513
|
+
|
|
514
|
+
return user;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Domain Layer
|
|
519
|
+
// user.entity.ts
|
|
520
|
+
export class User {
|
|
521
|
+
private constructor(
|
|
522
|
+
public readonly id: string,
|
|
523
|
+
private _name: string,
|
|
524
|
+
private _email: Email
|
|
525
|
+
) {}
|
|
526
|
+
|
|
527
|
+
static create(name: string, email: Email): User {
|
|
528
|
+
return new User(generateId(), name, email);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
updateName(newName: string): void {
|
|
532
|
+
if (newName.length < 2) {
|
|
533
|
+
throw new Error('Name too short');
|
|
534
|
+
}
|
|
535
|
+
this._name = newName;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Infrastructure Layer
|
|
540
|
+
// prisma-user.repository.ts
|
|
541
|
+
export class PrismaUserRepository implements UserRepository {
|
|
542
|
+
constructor(private prisma: PrismaClient) {}
|
|
543
|
+
|
|
544
|
+
async save(user: User): Promise<void> {
|
|
545
|
+
await this.prisma.user.create({
|
|
546
|
+
data: {
|
|
547
|
+
id: user.id,
|
|
548
|
+
name: user.name,
|
|
549
|
+
email: user.email.toString()
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## Clean Architecture
|
|
559
|
+
|
|
560
|
+
Uncle Bob's Clean Architecture emphasizes independence from frameworks, UI, and databases.
|
|
561
|
+
|
|
562
|
+
```
|
|
563
|
+
┌───────────────────────────────────────┐
|
|
564
|
+
│ Frameworks & Drivers │ ← Web, DB, External APIs
|
|
565
|
+
│ ┌─────────────────────────────────┐ │
|
|
566
|
+
│ │ Interface Adapters │ │ ← Controllers, Presenters, Gateways
|
|
567
|
+
│ │ ┌───────────────────────────┐ │ │
|
|
568
|
+
│ │ │ Application Business │ │ │ ← Use Cases
|
|
569
|
+
│ │ │ ┌─────────────────┐ │ │ │
|
|
570
|
+
│ │ │ │ Enterprise │ │ │ │ ← Entities
|
|
571
|
+
│ │ │ │ Business │ │ │ │
|
|
572
|
+
│ │ │ │ Rules │ │ │ │
|
|
573
|
+
│ │ │ └─────────────────┘ │ │ │
|
|
574
|
+
│ │ └───────────────────────────┘ │ │
|
|
575
|
+
│ └─────────────────────────────────┘ │
|
|
576
|
+
└───────────────────────────────────────┘
|
|
577
|
+
|
|
578
|
+
Dependencies point inward →
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Implementation
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
// Entities (Core)
|
|
585
|
+
// entities/user.ts
|
|
586
|
+
export class User {
|
|
587
|
+
constructor(
|
|
588
|
+
public readonly id: string,
|
|
589
|
+
public readonly email: string,
|
|
590
|
+
public readonly name: string
|
|
591
|
+
) {}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Use Cases (Application Business Rules)
|
|
595
|
+
// use-cases/create-user.ts
|
|
596
|
+
export interface CreateUserInput {
|
|
597
|
+
email: string;
|
|
598
|
+
name: string;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
export interface CreateUserOutput {
|
|
602
|
+
id: string;
|
|
603
|
+
email: string;
|
|
604
|
+
name: string;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
export interface UserGateway {
|
|
608
|
+
save(user: User): Promise<void>;
|
|
609
|
+
findByEmail(email: string): Promise<User | null>;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export class CreateUserUseCase {
|
|
613
|
+
constructor(private userGateway: UserGateway) {}
|
|
614
|
+
|
|
615
|
+
async execute(input: CreateUserInput): Promise<CreateUserOutput> {
|
|
616
|
+
const existing = await this.userGateway.findByEmail(input.email);
|
|
617
|
+
if (existing) {
|
|
618
|
+
throw new Error('Email already exists');
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const user = new User(generateId(), input.email, input.name);
|
|
622
|
+
await this.userGateway.save(user);
|
|
623
|
+
|
|
624
|
+
return {
|
|
625
|
+
id: user.id,
|
|
626
|
+
email: user.email,
|
|
627
|
+
name: user.name
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Interface Adapters
|
|
633
|
+
// adapters/user-controller.ts
|
|
634
|
+
export class UserController {
|
|
635
|
+
constructor(private createUserUseCase: CreateUserUseCase) {}
|
|
636
|
+
|
|
637
|
+
async create(req: Request, res: Response): Promise<void> {
|
|
638
|
+
const input: CreateUserInput = {
|
|
639
|
+
email: req.body.email,
|
|
640
|
+
name: req.body.name
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
const output = await this.createUserUseCase.execute(input);
|
|
644
|
+
res.status(201).json(output);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Frameworks & Drivers
|
|
649
|
+
// infrastructure/prisma-user-gateway.ts
|
|
650
|
+
export class PrismaUserGateway implements UserGateway {
|
|
651
|
+
constructor(private prisma: PrismaClient) {}
|
|
652
|
+
|
|
653
|
+
async save(user: User): Promise<void> {
|
|
654
|
+
await this.prisma.user.create({
|
|
655
|
+
data: { id: user.id, email: user.email, name: user.name }
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
async findByEmail(email: string): Promise<User | null> {
|
|
660
|
+
const data = await this.prisma.user.findUnique({ where: { email } });
|
|
661
|
+
return data ? new User(data.id, data.email, data.name) : null;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
---
|
|
667
|
+
|
|
668
|
+
## Hexagonal Architecture
|
|
669
|
+
|
|
670
|
+
Also known as Ports and Adapters. The domain is at the center, isolated from external concerns.
|
|
671
|
+
|
|
672
|
+
```
|
|
673
|
+
┌─────────────────────┐
|
|
674
|
+
│ HTTP Adapter │
|
|
675
|
+
│ (Controller) │
|
|
676
|
+
└──────────┬──────────┘
|
|
677
|
+
│
|
|
678
|
+
┌──────────▼──────────┐
|
|
679
|
+
│ Port │
|
|
680
|
+
│ (Interface) │
|
|
681
|
+
└──────────┬──────────┘
|
|
682
|
+
│
|
|
683
|
+
┌───────────────▼───────────────┐
|
|
684
|
+
│ │
|
|
685
|
+
│ Domain (Core) │
|
|
686
|
+
│ Business Logic │
|
|
687
|
+
│ │
|
|
688
|
+
└───────────────┬───────────────┘
|
|
689
|
+
│
|
|
690
|
+
┌──────────▼──────────┐
|
|
691
|
+
│ Port │
|
|
692
|
+
│ (Interface) │
|
|
693
|
+
└──────────┬──────────┘
|
|
694
|
+
│
|
|
695
|
+
┌──────────▼──────────┐
|
|
696
|
+
│ Database Adapter │
|
|
697
|
+
│ (Repository) │
|
|
698
|
+
└─────────────────────┘
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
### Implementation
|
|
702
|
+
|
|
703
|
+
```typescript
|
|
704
|
+
// Domain (Core)
|
|
705
|
+
// domain/user.ts
|
|
706
|
+
export class User {
|
|
707
|
+
constructor(
|
|
708
|
+
public readonly id: string,
|
|
709
|
+
private _email: string,
|
|
710
|
+
private _name: string
|
|
711
|
+
) {}
|
|
712
|
+
|
|
713
|
+
updateEmail(newEmail: string): void {
|
|
714
|
+
if (!this.isValidEmail(newEmail)) {
|
|
715
|
+
throw new Error('Invalid email');
|
|
716
|
+
}
|
|
717
|
+
this._email = newEmail;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
private isValidEmail(email: string): boolean {
|
|
721
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
get email(): string {
|
|
725
|
+
return this._email;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
get name(): string {
|
|
729
|
+
return this._name;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// domain/user.service.ts
|
|
734
|
+
export class UserService {
|
|
735
|
+
constructor(
|
|
736
|
+
private userRepository: UserRepositoryPort,
|
|
737
|
+
private emailService: EmailServicePort
|
|
738
|
+
) {}
|
|
739
|
+
|
|
740
|
+
async createUser(email: string, name: string): Promise<User> {
|
|
741
|
+
const existing = await this.userRepository.findByEmail(email);
|
|
742
|
+
if (existing) {
|
|
743
|
+
throw new Error('Email already exists');
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const user = new User(generateId(), email, name);
|
|
747
|
+
await this.userRepository.save(user);
|
|
748
|
+
await this.emailService.sendWelcomeEmail(user.email);
|
|
749
|
+
|
|
750
|
+
return user;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Ports (Interfaces)
|
|
755
|
+
// ports/user-repository.port.ts
|
|
756
|
+
export interface UserRepositoryPort {
|
|
757
|
+
findById(id: string): Promise<User | null>;
|
|
758
|
+
findByEmail(email: string): Promise<User | null>;
|
|
759
|
+
save(user: User): Promise<void>;
|
|
760
|
+
delete(id: string): Promise<void>;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// ports/email-service.port.ts
|
|
764
|
+
export interface EmailServicePort {
|
|
765
|
+
sendWelcomeEmail(email: string): Promise<void>;
|
|
766
|
+
sendPasswordReset(email: string, token: string): Promise<void>;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Adapters (Implementations)
|
|
770
|
+
// adapters/prisma-user.repository.ts
|
|
771
|
+
export class PrismaUserRepository implements UserRepositoryPort {
|
|
772
|
+
constructor(private prisma: PrismaClient) {}
|
|
773
|
+
|
|
774
|
+
async findById(id: string): Promise<User | null> {
|
|
775
|
+
const data = await this.prisma.user.findUnique({ where: { id } });
|
|
776
|
+
return data ? new User(data.id, data.email, data.name) : null;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
async findByEmail(email: string): Promise<User | null> {
|
|
780
|
+
const data = await this.prisma.user.findUnique({ where: { email } });
|
|
781
|
+
return data ? new User(data.id, data.email, data.name) : null;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
async save(user: User): Promise<void> {
|
|
785
|
+
await this.prisma.user.upsert({
|
|
786
|
+
where: { id: user.id },
|
|
787
|
+
create: { id: user.id, email: user.email, name: user.name },
|
|
788
|
+
update: { email: user.email, name: user.name }
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
async delete(id: string): Promise<void> {
|
|
793
|
+
await this.prisma.user.delete({ where: { id } });
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// adapters/sendgrid-email.service.ts
|
|
798
|
+
export class SendGridEmailService implements EmailServicePort {
|
|
799
|
+
constructor(private apiKey: string) {}
|
|
800
|
+
|
|
801
|
+
async sendWelcomeEmail(email: string): Promise<void> {
|
|
802
|
+
// SendGrid implementation
|
|
803
|
+
await sendEmail({
|
|
804
|
+
to: email,
|
|
805
|
+
subject: 'Welcome!',
|
|
806
|
+
body: 'Welcome to our platform'
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
async sendPasswordReset(email: string, token: string): Promise<void> {
|
|
811
|
+
// SendGrid implementation
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// adapters/http/user.controller.ts
|
|
816
|
+
export class UserController {
|
|
817
|
+
constructor(private userService: UserService) {}
|
|
818
|
+
|
|
819
|
+
async create(req: Request, res: Response): Promise<void> {
|
|
820
|
+
const { email, name } = req.body;
|
|
821
|
+
const user = await this.userService.createUser(email, name);
|
|
822
|
+
res.status(201).json({
|
|
823
|
+
id: user.id,
|
|
824
|
+
email: user.email,
|
|
825
|
+
name: user.name
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Dependency Injection Setup
|
|
831
|
+
// di-container.ts
|
|
832
|
+
const prisma = new PrismaClient();
|
|
833
|
+
const userRepository = new PrismaUserRepository(prisma);
|
|
834
|
+
const emailService = new SendGridEmailService(process.env.SENDGRID_API_KEY);
|
|
835
|
+
const userService = new UserService(userRepository, emailService);
|
|
836
|
+
const userController = new UserController(userService);
|
|
837
|
+
|
|
838
|
+
export { userController };
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
### Benefits of Hexagonal Architecture
|
|
842
|
+
|
|
843
|
+
1. **Testability**: Easy to test domain logic in isolation
|
|
844
|
+
2. **Flexibility**: Swap adapters without changing domain
|
|
845
|
+
3. **Independence**: Domain doesn't depend on frameworks
|
|
846
|
+
4. **Maintainability**: Clear separation of concerns
|
|
847
|
+
|
|
848
|
+
```typescript
|
|
849
|
+
// Easy to test with mocks
|
|
850
|
+
describe('UserService', () => {
|
|
851
|
+
it('should create user', async () => {
|
|
852
|
+
const mockRepository: UserRepositoryPort = {
|
|
853
|
+
findByEmail: vi.fn().mockResolvedValue(null),
|
|
854
|
+
save: vi.fn().mockResolvedValue(undefined),
|
|
855
|
+
findById: vi.fn(),
|
|
856
|
+
delete: vi.fn()
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
const mockEmailService: EmailServicePort = {
|
|
860
|
+
sendWelcomeEmail: vi.fn().mockResolvedValue(undefined),
|
|
861
|
+
sendPasswordReset: vi.fn()
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
const service = new UserService(mockRepository, mockEmailService);
|
|
865
|
+
const user = await service.createUser('test@example.com', 'Test User');
|
|
866
|
+
|
|
867
|
+
expect(user.email).toBe('test@example.com');
|
|
868
|
+
expect(mockRepository.save).toHaveBeenCalled();
|
|
869
|
+
expect(mockEmailService.sendWelcomeEmail).toHaveBeenCalled();
|
|
870
|
+
});
|
|
871
|
+
});
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
---
|
|
875
|
+
|
|
876
|
+
## Best Practices
|
|
877
|
+
|
|
878
|
+
### DO ✅
|
|
879
|
+
|
|
880
|
+
**Use folder-by-feature for scalability**
|
|
881
|
+
```typescript
|
|
882
|
+
// ✅ Good - Related code together
|
|
883
|
+
src/
|
|
884
|
+
├── users/
|
|
885
|
+
│ ├── user.entity.ts
|
|
886
|
+
│ ├── user.service.ts
|
|
887
|
+
│ ├── user.controller.ts
|
|
888
|
+
│ └── user.repository.ts
|
|
889
|
+
└── products/
|
|
890
|
+
├── product.entity.ts
|
|
891
|
+
├── product.service.ts
|
|
892
|
+
└── product.controller.ts
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
**Apply dependency inversion**
|
|
896
|
+
```typescript
|
|
897
|
+
// ✅ Good - Depend on abstractions
|
|
898
|
+
class UserService {
|
|
899
|
+
constructor(private repository: UserRepository) {} // Interface
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// ❌ Bad - Depend on concrete implementation
|
|
903
|
+
class UserService {
|
|
904
|
+
private repository = new MySQLUserRepository(); // Concrete class
|
|
905
|
+
}
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
**Keep domain logic pure**
|
|
909
|
+
```typescript
|
|
910
|
+
// ✅ Good - Pure domain logic
|
|
911
|
+
class Order {
|
|
912
|
+
calculateTotal(): Money {
|
|
913
|
+
return this.items.reduce(
|
|
914
|
+
(sum, item) => sum.add(item.price),
|
|
915
|
+
Money.zero()
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// ❌ Bad - Domain depends on infrastructure
|
|
921
|
+
class Order {
|
|
922
|
+
async calculateTotal(): Promise<Money> {
|
|
923
|
+
const prices = await database.query('SELECT price FROM items'); // DB dependency
|
|
924
|
+
return prices.reduce((sum, p) => sum + p, 0);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
**Use value objects for domain concepts**
|
|
930
|
+
```typescript
|
|
931
|
+
// ✅ Good - Value object
|
|
932
|
+
class Email {
|
|
933
|
+
private constructor(private value: string) {}
|
|
934
|
+
|
|
935
|
+
static create(email: string): Email {
|
|
936
|
+
if (!this.isValid(email)) throw new Error('Invalid email');
|
|
937
|
+
return new Email(email);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// ❌ Bad - Primitive obsession
|
|
942
|
+
function createUser(email: string) { // Just a string
|
|
943
|
+
// No validation, no encapsulation
|
|
944
|
+
}
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
**Separate use cases from domain logic**
|
|
948
|
+
```typescript
|
|
949
|
+
// ✅ Good - Use case orchestrates
|
|
950
|
+
class CreateUserUseCase {
|
|
951
|
+
async execute(dto: CreateUserDto): Promise<User> {
|
|
952
|
+
const user = User.create(dto.name, dto.email);
|
|
953
|
+
await this.repository.save(user);
|
|
954
|
+
await this.emailService.sendWelcome(user.email);
|
|
955
|
+
return user;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// ❌ Bad - Domain entity does too much
|
|
960
|
+
class User {
|
|
961
|
+
async create(name: string, email: string) {
|
|
962
|
+
// Validation
|
|
963
|
+
// Database save
|
|
964
|
+
// Send email
|
|
965
|
+
// Too many responsibilities
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
### DON'T ❌
|
|
971
|
+
|
|
972
|
+
**Don't let domain depend on infrastructure**
|
|
973
|
+
```typescript
|
|
974
|
+
// ❌ Bad
|
|
975
|
+
class User {
|
|
976
|
+
async save() {
|
|
977
|
+
await prisma.user.create({ data: this }); // Domain depends on Prisma
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// ✅ Good
|
|
982
|
+
class User {
|
|
983
|
+
// Pure domain logic only
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
class UserRepository {
|
|
987
|
+
async save(user: User) {
|
|
988
|
+
await prisma.user.create({ data: this.toPersistence(user) });
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
**Don't mix layers**
|
|
994
|
+
```typescript
|
|
995
|
+
// ❌ Bad - Controller has business logic
|
|
996
|
+
class UserController {
|
|
997
|
+
async create(req: Request, res: Response) {
|
|
998
|
+
const user = new User(req.body.name, req.body.email);
|
|
999
|
+
if (await this.repository.findByEmail(user.email)) {
|
|
1000
|
+
throw new Error('Email exists'); // Business logic in controller
|
|
1001
|
+
}
|
|
1002
|
+
await this.repository.save(user);
|
|
1003
|
+
res.json(user);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// ✅ Good - Business logic in service
|
|
1008
|
+
class UserService {
|
|
1009
|
+
async createUser(name: string, email: string): Promise<User> {
|
|
1010
|
+
if (await this.repository.findByEmail(email)) {
|
|
1011
|
+
throw new Error('Email exists'); // Business logic in service
|
|
1012
|
+
}
|
|
1013
|
+
const user = new User(name, email);
|
|
1014
|
+
await this.repository.save(user);
|
|
1015
|
+
return user;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
**Don't create anemic domain models**
|
|
1021
|
+
```typescript
|
|
1022
|
+
// ❌ Bad - Anemic model (just data, no behavior)
|
|
1023
|
+
class User {
|
|
1024
|
+
id: string;
|
|
1025
|
+
email: string;
|
|
1026
|
+
name: string;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
class UserService {
|
|
1030
|
+
updateEmail(user: User, newEmail: string) {
|
|
1031
|
+
// All logic in service
|
|
1032
|
+
if (!this.isValidEmail(newEmail)) throw new Error('Invalid');
|
|
1033
|
+
user.email = newEmail;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// ✅ Good - Rich domain model
|
|
1038
|
+
class User {
|
|
1039
|
+
constructor(
|
|
1040
|
+
public readonly id: string,
|
|
1041
|
+
private _email: string,
|
|
1042
|
+
private _name: string
|
|
1043
|
+
) {}
|
|
1044
|
+
|
|
1045
|
+
updateEmail(newEmail: string): void {
|
|
1046
|
+
if (!this.isValidEmail(newEmail)) throw new Error('Invalid');
|
|
1047
|
+
this._email = newEmail;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
private isValidEmail(email: string): boolean {
|
|
1051
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
---
|
|
1057
|
+
|
|
1058
|
+
## Summary
|
|
1059
|
+
|
|
1060
|
+
**Key Takeaways:**
|
|
1061
|
+
|
|
1062
|
+
1. **Folder-by-feature** - Organize code by business domain, not technical role
|
|
1063
|
+
2. **Dependency inversion** - Depend on abstractions, not concrete implementations
|
|
1064
|
+
3. **Domain-driven design** - Model the business domain with entities, value objects, and aggregates
|
|
1065
|
+
4. **Layered architecture** - Separate presentation, application, domain, and infrastructure
|
|
1066
|
+
5. **Clean architecture** - Keep business rules independent of frameworks and UI
|
|
1067
|
+
6. **Hexagonal architecture** - Isolate domain with ports and adapters
|
|
1068
|
+
7. **Rich domain models** - Put business logic in domain entities, not services
|
|
1069
|
+
8. **Use cases** - Orchestrate domain logic and infrastructure
|
|
1070
|
+
9. **Testability** - Architecture should make testing easy
|
|
1071
|
+
10. **Flexibility** - Easy to swap implementations and adapt to change
|
|
1072
|
+
|
|
1073
|
+
**Architecture Checklist:**
|
|
1074
|
+
|
|
1075
|
+
- [ ] Code organized by feature/domain
|
|
1076
|
+
- [ ] Dependencies point inward (toward domain)
|
|
1077
|
+
- [ ] Domain logic is pure (no infrastructure dependencies)
|
|
1078
|
+
- [ ] Interfaces define contracts between layers
|
|
1079
|
+
- [ ] Use cases orchestrate application logic
|
|
1080
|
+
- [ ] Value objects encapsulate domain concepts
|
|
1081
|
+
- [ ] Repositories abstract data access
|
|
1082
|
+
- [ ] Easy to test in isolation
|
|
1083
|
+
- [ ] Easy to swap implementations
|
|
1084
|
+
- [ ] Clear separation of concerns
|
|
1085
|
+
|
|
1086
|
+
---
|
|
1087
|
+
|
|
1088
|
+
## References
|
|
1089
|
+
|
|
1090
|
+
- [Clean Architecture - Robert C. Martin](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
|
|
1091
|
+
- [Hexagonal Architecture - Alistair Cockburn](https://alistair.cockburn.us/hexagonal-architecture/)
|
|
1092
|
+
- [Domain-Driven Design - Eric Evans](https://www.domainlanguage.com/ddd/)
|
|
1093
|
+
- [SOLID Principles](https://en.wikipedia.org/wiki/SOLID)
|
|
1094
|
+
- [Dependency Inversion Principle](https://en.wikipedia.org/wiki/Dependency_inversion_principle)
|
|
1095
|
+
|
|
1096
|
+
|