@wazir-dev/cli 1.0.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/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,870 @@
|
|
|
1
|
+
# Hexagonal / Clean Architecture — Architecture Expertise Module
|
|
2
|
+
|
|
3
|
+
> Hexagonal Architecture (Ports & Adapters, Alistair Cockburn 2005) and Clean Architecture (Robert C. Martin 2012) are variations of the same idea: isolate business logic from infrastructure by inverting dependencies so the domain is the center that nothing depends on. This dramatically improves testability and allows swapping infrastructure without touching business rules.
|
|
4
|
+
|
|
5
|
+
> **Category:** Pattern
|
|
6
|
+
> **Complexity:** Complex
|
|
7
|
+
> **Applies when:** Long-lived systems with complex domain logic that needs to survive framework and infrastructure changes
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What This Is (and What It Isn't)
|
|
12
|
+
|
|
13
|
+
### The Three Sibling Patterns
|
|
14
|
+
|
|
15
|
+
**Hexagonal Architecture (Ports & Adapters)** — Alistair Cockburn coined this in 1994, published it in 2005. The "hexagon" shape has no significance beyond having enough sides to draw adapters around; Cockburn himself said a circle would have worked. The key idea: the application has a center (domain + use cases) surrounded by ports. A **port** is an interface — a declared contract for how the outside world interacts with the domain. An **adapter** is a concrete implementation of a port — a database repository, an HTTP controller, a CLI handler, a test double. Dependencies point inward. The core never imports the adapters.
|
|
16
|
+
|
|
17
|
+
**Clean Architecture** — Robert C. Martin (2012) drew concentric rings: Entities (innermost) → Use Cases → Interface Adapters → Frameworks & Drivers (outermost). The same dependency rule: source code dependencies point only inward. Clean Architecture added naming clarity: "entities" carry enterprise-wide business rules, "use cases" carry application-specific business rules. The outermost ring is where frameworks live and is explicitly treated as a detail.
|
|
18
|
+
|
|
19
|
+
**Onion Architecture** — Jeffrey Palermo (2008). Structurally similar. Emphasizes the Domain Model as the absolute center, with Domain Services wrapping it, then Application Services, then Infrastructure on the outside. Onion does not prescribe the Port/Adapter mechanism explicitly — it achieves inversion of control but leaves the "how" to the implementer, whereas Hexagonal names the boundary mechanism (ports) explicitly.
|
|
20
|
+
|
|
21
|
+
### What They Share
|
|
22
|
+
|
|
23
|
+
All three patterns implement the same core principle: **the Dependency Rule**. In every variation:
|
|
24
|
+
|
|
25
|
+
- The domain (business logic) has zero imports from infrastructure, frameworks, or delivery mechanisms.
|
|
26
|
+
- Infrastructure depends on the domain, not the other way around.
|
|
27
|
+
- The domain is testable in complete isolation — no database, no HTTP server, no framework.
|
|
28
|
+
- Frameworks, databases, and delivery mechanisms are treated as **interchangeable details**.
|
|
29
|
+
|
|
30
|
+
### What They Are Not
|
|
31
|
+
|
|
32
|
+
**Not a folder structure.** The most common beginner mistake is to organize folders as `domain/`, `application/`, `infrastructure/` and assume that constitutes clean architecture. Folder names mean nothing if a class in `domain/` still imports `sequelize` or Spring annotations. The architecture is defined by dependency direction, not directory names.
|
|
33
|
+
|
|
34
|
+
**Not a guarantee of clean code.** You can implement all the layers perfectly and still write an anemic domain, duplicated logic, and unmaintainable use cases. The pattern enforces structural constraints, not quality of thought.
|
|
35
|
+
|
|
36
|
+
**Not required for all projects.** This is a load-bearing architectural investment. It pays dividends over years, not sprints. Applied to the wrong problem it becomes pure overhead.
|
|
37
|
+
|
|
38
|
+
**Not the only way to achieve testability.** A well-factored layered architecture with careful import discipline can also be tested without infrastructure. The hexagonal approach formalizes this into an enforced constraint.
|
|
39
|
+
|
|
40
|
+
### Nomenclature Differences (Cheat Sheet)
|
|
41
|
+
|
|
42
|
+
| Concept | Hexagonal | Clean Architecture | Onion |
|
|
43
|
+
|---|---|---|---|
|
|
44
|
+
| Business objects | Domain Entities | Entities | Domain Model |
|
|
45
|
+
| Business operations | Domain Services | Use Cases | Domain Services |
|
|
46
|
+
| Boundary interfaces | Ports | Interface Adapters (partially) | Application Services interfaces |
|
|
47
|
+
| Concrete integrations | Adapters | Interface Adapters (concrete) | Infrastructure |
|
|
48
|
+
| Outermost ring | Adapters (driving + driven) | Frameworks & Drivers | Infrastructure |
|
|
49
|
+
|
|
50
|
+
The differences are mostly nomenclature and emphasis. In practice, teams mix terms freely. "Hexagonal" tends to be used when the focus is on testability and adapter swapping. "Clean Architecture" is used when the focus is on ring boundaries and use case visibility.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## When to Use It
|
|
55
|
+
|
|
56
|
+
### The Qualifying Conditions
|
|
57
|
+
|
|
58
|
+
Apply hexagonal/clean architecture when **two or more** of these are true:
|
|
59
|
+
|
|
60
|
+
**Long-lived application.** The payoff from establishing dependency inversion and adapter interfaces materializes over 2–5 years as the domain evolves. If the project horizon is under 18 months, the setup cost is hard to justify.
|
|
61
|
+
|
|
62
|
+
**Complex domain logic.** The domain has rules that change for business reasons independent from how data is stored or delivered. Loan eligibility calculations, insurance underwriting, healthcare treatment protocols, financial settlement rules — these change because regulations or business strategy change, not because the database schema changed. If your "business rules" always change when the DB schema changes, your "domain" is a data access layer wearing a costume.
|
|
63
|
+
|
|
64
|
+
**Independent testability is a hard requirement.** The team needs a test suite that runs in milliseconds without a database or external service. This is a prerequisite for fast CI, TDD, and confident refactoring. Hexagonal makes this structurally guaranteed rather than dependent on discipline.
|
|
65
|
+
|
|
66
|
+
**Infrastructure replacement is realistic.** You are migrating from one ORM to another. You need to support both PostgreSQL and an in-memory store for testing. You need to expose the same use cases via REST and gRPC. You plan to move from a monolith to microservices over time. Each of these migrations is substantially cheaper when adapters are the only thing that changes.
|
|
67
|
+
|
|
68
|
+
**Multiple delivery mechanisms.** A system that must serve HTTP REST, gRPC, and a scheduled job runner simultaneously benefits from a shared use case layer that all three delivery mechanisms call. Without hexagonal, the same business logic gets duplicated into each delivery path.
|
|
69
|
+
|
|
70
|
+
### Real-World Contexts Where This Pays Off
|
|
71
|
+
|
|
72
|
+
**Complex financial systems.** Payment processors like PayPal separate core transaction processing from banking APIs, fraud detection services, and user interfaces through adapters. This allows them to adapt to regulatory changes and new banking integrations without touching settlement logic. The driven ports (banking API, fraud service) can be replaced without touching the transaction domain.
|
|
73
|
+
|
|
74
|
+
**Healthcare applications.** Healthcare systems face strict regulatory requirements that are independent from whether records are stored in SQL Server or MongoDB, or whether the UI is a React SPA or a native mobile app. The domain rules (clinical decision logic, formulary checks, HIPAA-compliant access control) need to be testable in complete isolation from storage and delivery.
|
|
75
|
+
|
|
76
|
+
**E-commerce platforms.** Shopify's architecture decouples its core commerce logic from payment gateways (Stripe, PayPal, local gateways), shipping providers, and storefront delivery mechanisms. A merchant switching payment processors touches only the adapter, not the checkout domain.
|
|
77
|
+
|
|
78
|
+
**Systems requiring dual-protocol exposure.** A financial data service that must serve both REST and gRPC, or a notification system that must deliver via email, SMS, and push notification, can share a single application layer with adapters per protocol/channel.
|
|
79
|
+
|
|
80
|
+
**Platform migrations.** A system that started on a legacy ORM (Hibernate, ActiveRecord) and needs to migrate to a different persistence mechanism can do so adapter-by-adapter if repository interfaces were established from the start.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## When NOT to Use It
|
|
85
|
+
|
|
86
|
+
This section is as important as the previous one. Hexagonal architecture is frequently applied to systems that do not warrant it, producing unnecessary complexity, slower development, and justified resentment from teams who then dismiss the pattern entirely.
|
|
87
|
+
|
|
88
|
+
### The Disqualifying Conditions
|
|
89
|
+
|
|
90
|
+
**Simple CRUD applications.** If the application is fundamentally reading data, persisting data, and returning it — with little to no business logic in between — then hexagonal architecture adds indirection with no return. A `UserController` that calls a `UserService` that calls a `UserRepository` is three layers for a DB write. Adding ports and adapters turns this into five or six hops through no real logic. A startup configuring Clean Architecture for an internal admin dashboard spent three months establishing the layer structure and shipped features at a fraction of the expected velocity. The domain had no rules. There was nothing to protect.
|
|
91
|
+
|
|
92
|
+
**Fewer than 10 entities with thin business logic.** A blog engine with posts, tags, authors, and comments has almost no domain logic that is independent of how it is stored. The "business rules" are: a post belongs to one author, a post has tags. These are data constraints, not domain logic. Implementing hexagonal architecture here means writing a `PostRepository` port, a `PostRepositoryImpl`, a `PostService`, a `CreatePostUseCase`, a `CreatePostRequest` DTO, and a `CreatePostResponse` DTO — to do one INSERT. The indirection is not earned.
|
|
93
|
+
|
|
94
|
+
**The "no reason to change independently" test.** Ask: does the business logic change for reasons independent from infrastructure changes? If every time a new database column is added, a business rule changes, and every time a business rule changes, a column is added — the domain and the infrastructure are the same thing. You have a CRUD application with a rich domain costume. The architecture cannot decouple what is fundamentally coupled.
|
|
95
|
+
|
|
96
|
+
**Prototypes and MVPs.** The explicit purpose of a prototype is to test assumptions quickly before committing. Clean architecture slows the feedback loop. Establish the simplest possible structure, validate the idea, then introduce architecture when the system earns it.
|
|
97
|
+
|
|
98
|
+
**Short-lived scripts and tooling.** Data migration scripts, one-off reporting tools, CLI utilities with a 6-month lifespan: the overhead of port/adapter design produces no return.
|
|
99
|
+
|
|
100
|
+
**Admin panels and back-office tools.** These are typically thin wrappers around a database with authorization rules. The "business logic" is largely query filters and permission checks. The test burden is on integration and UI tests, not domain unit tests.
|
|
101
|
+
|
|
102
|
+
**ETL pipelines and data transformation jobs.** When the job *is* the data transformation, there is no domain separate from the transformation. The "business rule" is the shape of the transformation. Hexagonal architecture applied here produces ports for transformations that will never have a second adapter.
|
|
103
|
+
|
|
104
|
+
**Team size below 3 and horizon under 12 months.** The cognitive overhead of hexagonal architecture is real. A solo developer or a team of two building something with a defined end-date is paying the overhead with no team coordination or long-term maintenance benefit to show for it.
|
|
105
|
+
|
|
106
|
+
### The Incident Pattern
|
|
107
|
+
|
|
108
|
+
A recurring failure mode observed in engineering teams: a senior engineer who has seen hexagonal architecture succeed in a complex system applies it to the next project regardless of context. The next project is a SaaS admin portal with 8 entities and no business logic. Six weeks into development the team has a perfect adapter layer but is behind schedule, the junior engineers are confused by the indirection, and every feature requires creating 5–7 files. The architecture is technically correct and practically counterproductive.
|
|
109
|
+
|
|
110
|
+
The corrective heuristic: if you cannot name at least three business rules that (a) have genuine complexity, (b) are tested without a DB today or need to be, and (c) are independent of infrastructure concerns, do not use hexagonal architecture.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## How It Works
|
|
115
|
+
|
|
116
|
+
### Layer Definitions
|
|
117
|
+
|
|
118
|
+
**Domain Layer** — The innermost ring. Contains:
|
|
119
|
+
- **Entities**: Objects with identity and lifecycle. They encapsulate enterprise-wide business rules. A `LoanApplication` entity knows whether it is eligible for approval. A `BankAccount` entity enforces that its balance cannot go below zero in a given account type. An `Order` entity knows whether it can be cancelled given its current state.
|
|
120
|
+
- **Value Objects**: Immutable objects defined by their attributes, not identity. A `Money` value object prevents arithmetic errors between currencies. An `EmailAddress` value object validates format at construction. A `DateRange` value object enforces start-before-end invariants.
|
|
121
|
+
- **Domain Services**: Logic that does not naturally belong to a single entity — e.g., a `TransferService` that operates on two `BankAccount` entities.
|
|
122
|
+
- **Domain Events**: Signals emitted when something meaningful happens in the domain (`OrderPlaced`, `PaymentFailed`).
|
|
123
|
+
- **Repository Interfaces (Ports)**: The domain layer declares the interfaces it needs for persistence. It does not implement them. `OrderRepository` is an interface in the domain. The implementation lives in infrastructure.
|
|
124
|
+
|
|
125
|
+
The domain layer has **zero imports from infrastructure, frameworks, HTTP, databases, or ORMs**. No Spring annotations, no Sequelize models, no SQLAlchemy declarative base, no HTTP status codes.
|
|
126
|
+
|
|
127
|
+
**Application Layer** — Use cases / application services. Contains:
|
|
128
|
+
- **Use Cases (Application Services)**: One use case per meaningful operation. `PlaceOrderUseCase` takes an `PlaceOrderCommand`, loads domain objects via repository ports, calls domain logic, persists changes through the port, and emits domain events. It coordinates; it does not contain business rules.
|
|
129
|
+
- **Port Interfaces (driven ports)**: Interfaces for external services that the application layer needs — email sender, payment gateway, notification service. These are defined here, implemented in infrastructure.
|
|
130
|
+
- **DTOs / Command / Query Objects**: Data structures that cross the application boundary. Commands carry intent (`PlaceOrderCommand`). Queries carry retrieval intent. DTOs carry data outward. They are dumb data containers with no business logic.
|
|
131
|
+
|
|
132
|
+
The application layer imports the domain layer. It imports nothing from infrastructure or delivery.
|
|
133
|
+
|
|
134
|
+
**Infrastructure Layer** — Adapters implementing driven ports. Contains:
|
|
135
|
+
- **Repository Implementations**: `PostgresOrderRepository implements OrderRepository`. Translates domain objects to/from database rows using whatever ORM or query builder the team chooses.
|
|
136
|
+
- **External Service Adapters**: `StripePaymentGateway implements PaymentGateway`. `SendgridEmailSender implements EmailSender`. `TwilioSmsSender implements SmsSender`.
|
|
137
|
+
- **Message Bus Adapters**: `KafkaEventPublisher implements DomainEventPublisher`.
|
|
138
|
+
- **ORM configurations, migrations, connection pools**: All infrastructure concerns live here and only here.
|
|
139
|
+
|
|
140
|
+
**Presentation / Delivery Layer** — Adapters implementing driving ports (or driving the application layer directly). Contains:
|
|
141
|
+
- **HTTP Controllers**: Parse HTTP requests, call use cases, map responses. They know HTTP. They do not know business rules.
|
|
142
|
+
- **gRPC handlers, CLI handlers, GraphQL resolvers**: All translate their protocol into use case calls.
|
|
143
|
+
- **Scheduled job runners**: Trigger use cases on a time schedule.
|
|
144
|
+
|
|
145
|
+
### The Dependency Rule in Practice
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
Presentation ──► Application ──► Domain
|
|
149
|
+
Infrastructure ──► Application ──► Domain
|
|
150
|
+
(never the reverse)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
If a domain entity needs to send an email, it does not import an email library. It raises a domain event or calls an interface defined in the domain/application layer. The infrastructure adapter resolves the interface at runtime through dependency injection.
|
|
154
|
+
|
|
155
|
+
### Ports: Driven vs Driving
|
|
156
|
+
|
|
157
|
+
**Driven ports (right-hand side / secondary adapters):** Interfaces the application drives — databases, email services, payment gateways, external APIs, message queues. The application defines these interfaces based on what it needs. The infrastructure implements them.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
// Defined in application layer — this is a driven port
|
|
161
|
+
interface OrderRepository {
|
|
162
|
+
findById(id: OrderId): Promise<Order | null>;
|
|
163
|
+
save(order: Order): Promise<void>;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Defined in application layer — driven port for external service
|
|
167
|
+
interface PaymentGateway {
|
|
168
|
+
charge(amount: Money, source: PaymentSource): Promise<PaymentResult>;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Driving ports (left-hand side / primary adapters):** Interfaces through which the outside world drives the application. In practice, these are often just the use case interfaces themselves. HTTP controllers call use cases directly through their interfaces.
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// Defined in application layer — this is a driving port
|
|
176
|
+
interface PlaceOrderUseCase {
|
|
177
|
+
execute(command: PlaceOrderCommand): Promise<PlaceOrderResult>;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### How a Use Case Executes
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// application/use-cases/place-order.ts
|
|
185
|
+
class PlaceOrderUseCaseImpl implements PlaceOrderUseCase {
|
|
186
|
+
constructor(
|
|
187
|
+
private readonly orderRepository: OrderRepository, // driven port
|
|
188
|
+
private readonly paymentGateway: PaymentGateway, // driven port
|
|
189
|
+
private readonly eventPublisher: DomainEventPublisher // driven port
|
|
190
|
+
) {}
|
|
191
|
+
|
|
192
|
+
async execute(command: PlaceOrderCommand): Promise<PlaceOrderResult> {
|
|
193
|
+
// Load domain objects through the port (never the concrete implementation)
|
|
194
|
+
const customer = await this.customerRepository.findById(command.customerId);
|
|
195
|
+
const items = command.items.map(i => OrderItem.create(i.productId, i.quantity, i.price));
|
|
196
|
+
|
|
197
|
+
// Domain logic: the Order entity enforces its own rules
|
|
198
|
+
const order = Order.place(customer, items); // throws if customer ineligible
|
|
199
|
+
|
|
200
|
+
// Driven port: persist through the interface
|
|
201
|
+
await this.orderRepository.save(order);
|
|
202
|
+
|
|
203
|
+
// Driven port: charge through the interface
|
|
204
|
+
const paymentResult = await this.paymentGateway.charge(
|
|
205
|
+
order.totalAmount,
|
|
206
|
+
command.paymentSource
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
if (!paymentResult.succeeded) {
|
|
210
|
+
order.markPaymentFailed(paymentResult.failureReason);
|
|
211
|
+
await this.orderRepository.save(order);
|
|
212
|
+
return PlaceOrderResult.failure(paymentResult.failureReason);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
order.confirm();
|
|
216
|
+
await this.orderRepository.save(order);
|
|
217
|
+
|
|
218
|
+
// Domain event: raised by the entity, published by the application layer
|
|
219
|
+
for (const event of order.domainEvents) {
|
|
220
|
+
await this.eventPublisher.publish(event);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return PlaceOrderResult.success(order.id);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The use case has no knowledge of PostgreSQL, Stripe, Kafka, or HTTP. It calls interfaces. A test can inject in-memory implementations of all three ports and test the full use case logic in microseconds.
|
|
229
|
+
|
|
230
|
+
### How Testing Works
|
|
231
|
+
|
|
232
|
+
**Domain tests — zero mocks, pure logic:**
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// test/domain/order.test.ts
|
|
236
|
+
describe('Order', () => {
|
|
237
|
+
it('cannot place an order for an inactive customer', () => {
|
|
238
|
+
const inactiveCustomer = Customer.createInactive('c-1');
|
|
239
|
+
const items = [OrderItem.create('p-1', 1, Money.of(100, 'USD'))];
|
|
240
|
+
expect(() => Order.place(inactiveCustomer, items)).toThrow(CustomerInactiveError);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
No database. No HTTP. No mocks. Pure TypeScript classes. Runs in < 1ms.
|
|
246
|
+
|
|
247
|
+
**Application layer tests — mock adapters (test doubles):**
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// test/application/place-order.test.ts
|
|
251
|
+
describe('PlaceOrderUseCase', () => {
|
|
252
|
+
it('marks order as failed if payment is declined', async () => {
|
|
253
|
+
const orderRepo = new InMemoryOrderRepository();
|
|
254
|
+
const paymentGateway = new AlwaysDeclinedPaymentGateway();
|
|
255
|
+
const eventPublisher = new RecordingEventPublisher();
|
|
256
|
+
|
|
257
|
+
const useCase = new PlaceOrderUseCaseImpl(orderRepo, paymentGateway, eventPublisher);
|
|
258
|
+
const result = await useCase.execute(validCommand);
|
|
259
|
+
|
|
260
|
+
expect(result.succeeded).toBe(false);
|
|
261
|
+
const savedOrder = await orderRepo.findById(result.orderId);
|
|
262
|
+
expect(savedOrder.status).toBe(OrderStatus.PaymentFailed);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The `InMemoryOrderRepository`, `AlwaysDeclinedPaymentGateway`, and `RecordingEventPublisher` are not mocks from a mocking framework — they are full implementations of the port interfaces, backed by in-memory data structures. They are fast, deterministic, and reusable across many tests.
|
|
268
|
+
|
|
269
|
+
**Infrastructure tests — integration tests, testing the adapter alone:**
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
// test/infrastructure/postgres-order-repository.test.ts
|
|
273
|
+
// This test requires a real (or Dockerized) Postgres instance
|
|
274
|
+
describe('PostgresOrderRepository', () => {
|
|
275
|
+
it('persists and retrieves an order', async () => {
|
|
276
|
+
const repo = new PostgresOrderRepository(testConnection);
|
|
277
|
+
const order = Order.place(activeCustomer, validItems);
|
|
278
|
+
await repo.save(order);
|
|
279
|
+
const retrieved = await repo.findById(order.id);
|
|
280
|
+
expect(retrieved).toEqual(order);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
This test is slow, requires infrastructure, and is run in CI — not in the local fast suite. It tests that the adapter correctly implements the port contract.
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Trade-Offs Matrix
|
|
290
|
+
|
|
291
|
+
| You Get | You Pay |
|
|
292
|
+
|---|---|
|
|
293
|
+
| Domain unit tests with zero infrastructure (milliseconds) | 3–5x more files for equivalent functionality |
|
|
294
|
+
| Infrastructure is swappable without touching business logic | Every new feature requires changes in 3–4 layers |
|
|
295
|
+
| Framework independence — the domain is a plain library | Dependency injection wiring complexity |
|
|
296
|
+
| Business rules are readable in isolation (use cases as documentation) | Significant cognitive overhead for new team members |
|
|
297
|
+
| Parallel development (domain team and infrastructure team can work independently) | DTO mapping boilerplate at every layer boundary |
|
|
298
|
+
| Slow tests become fast tests (domain tests don't hit DB) | Risk of anemic domain (logic drifts into use cases) |
|
|
299
|
+
| Framework upgrades are contained to the outermost ring | Port/interface design requires upfront thought — wrong ports are expensive to change |
|
|
300
|
+
| Multiple delivery mechanisms (REST + gRPC + CLI) share the same use cases | Over-engineering risk: teams apply it to CRUD apps and pay the cost without the benefit |
|
|
301
|
+
| System is auditable: use cases are explicit named operations | Initial delivery velocity is lower than a simple layered approach |
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Evolution Path
|
|
306
|
+
|
|
307
|
+
### Do Not Start With Hexagonal
|
|
308
|
+
|
|
309
|
+
The most reliable path to hexagonal architecture is to **earn it** through felt pain in a simpler architecture. Starting with hexagonal on a new project means paying the setup cost before you understand the problem domain — and the ports you define before you understand the domain are likely to be wrong.
|
|
310
|
+
|
|
311
|
+
### Stage 1: Simple Layered Architecture
|
|
312
|
+
|
|
313
|
+
Start with three layers: Controllers → Services → Repositories. No interfaces, no ports, no adapters. Just classes calling classes.
|
|
314
|
+
|
|
315
|
+
```
|
|
316
|
+
src/
|
|
317
|
+
controllers/
|
|
318
|
+
order.controller.ts
|
|
319
|
+
services/
|
|
320
|
+
order.service.ts
|
|
321
|
+
repositories/
|
|
322
|
+
order.repository.ts ← imports Sequelize/TypeORM directly
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
This is fast to build. It works for most projects at the start.
|
|
326
|
+
|
|
327
|
+
### Stage 2: Pain Points Emerge
|
|
328
|
+
|
|
329
|
+
After 6–12 months on a complex domain, the team starts feeling:
|
|
330
|
+
- "I can't unit test `order.service.ts` without a real database."
|
|
331
|
+
- "The service now imports `stripe` directly — how do I test payment failure without hitting Stripe?"
|
|
332
|
+
- "We need to switch from REST-only to also support gRPC — the business logic is buried in HTTP request handlers."
|
|
333
|
+
- "The domain rules are scattered across services, controllers, and utility functions."
|
|
334
|
+
|
|
335
|
+
These are the signals. The pain is diagnostic.
|
|
336
|
+
|
|
337
|
+
### Stage 3: Extract the Domain
|
|
338
|
+
|
|
339
|
+
Identify domain logic and move it into domain classes with no infrastructure imports:
|
|
340
|
+
|
|
341
|
+
```
|
|
342
|
+
src/
|
|
343
|
+
domain/
|
|
344
|
+
order.entity.ts ← business rules live here now
|
|
345
|
+
order-item.value-object.ts
|
|
346
|
+
customer.entity.ts
|
|
347
|
+
services/
|
|
348
|
+
order.service.ts ← orchestrates, calls domain methods
|
|
349
|
+
repositories/
|
|
350
|
+
order.repository.ts ← still concrete
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Tests can now cover domain logic without a database.
|
|
354
|
+
|
|
355
|
+
### Stage 4: Define Repository Interfaces (Invert the Dependency)
|
|
356
|
+
|
|
357
|
+
```
|
|
358
|
+
src/
|
|
359
|
+
domain/
|
|
360
|
+
order.entity.ts
|
|
361
|
+
order.repository.ts ← interface, defined here
|
|
362
|
+
services/
|
|
363
|
+
order.service.ts ← now depends on the interface
|
|
364
|
+
repositories/
|
|
365
|
+
postgres-order.repository.ts ← implements the interface
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Now `order.service.ts` can be tested with an in-memory implementation of `OrderRepository`.
|
|
369
|
+
|
|
370
|
+
### Stage 5: Full Hexagonal
|
|
371
|
+
|
|
372
|
+
Separate driven ports for all external services, formalize use cases, establish the application layer:
|
|
373
|
+
|
|
374
|
+
```
|
|
375
|
+
src/
|
|
376
|
+
domain/
|
|
377
|
+
orders/
|
|
378
|
+
order.entity.ts
|
|
379
|
+
order-item.value-object.ts
|
|
380
|
+
order.repository.ts ← port
|
|
381
|
+
payment-gateway.port.ts ← port
|
|
382
|
+
application/
|
|
383
|
+
use-cases/
|
|
384
|
+
place-order.usecase.ts
|
|
385
|
+
cancel-order.usecase.ts
|
|
386
|
+
infrastructure/
|
|
387
|
+
persistence/
|
|
388
|
+
postgres-order.repository.ts ← adapter
|
|
389
|
+
payment/
|
|
390
|
+
stripe-payment-gateway.ts ← adapter
|
|
391
|
+
presentation/
|
|
392
|
+
http/
|
|
393
|
+
order.controller.ts ← adapter
|
|
394
|
+
grpc/
|
|
395
|
+
order.grpc-handler.ts ← adapter
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Migration Checklist
|
|
399
|
+
|
|
400
|
+
1. Identify domain logic (look for business rule conditionals in services and repositories).
|
|
401
|
+
2. Move domain logic into entity/value object classes with no infrastructure imports.
|
|
402
|
+
3. Identify all external dependencies (DB, email, payment, third-party APIs).
|
|
403
|
+
4. Define an interface (port) for each external dependency in the domain or application layer.
|
|
404
|
+
5. Move concrete implementations behind those interfaces (create adapters).
|
|
405
|
+
6. Update dependency injection to wire adapters to ports.
|
|
406
|
+
7. Write domain unit tests — they should now run without infrastructure.
|
|
407
|
+
8. Write application layer tests with in-memory port implementations.
|
|
408
|
+
9. Move infrastructure tests to a separate, slower test suite.
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Failure Modes
|
|
413
|
+
|
|
414
|
+
### FM-01: Anemic Domain Model
|
|
415
|
+
|
|
416
|
+
**What it looks like:** Domain entities are bags of getters and setters. All logic lives in use cases and application services.
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
// Anemic — no business logic
|
|
420
|
+
class Order {
|
|
421
|
+
id: string;
|
|
422
|
+
status: string;
|
|
423
|
+
items: OrderItem[];
|
|
424
|
+
total: number;
|
|
425
|
+
// No methods. No invariants. No rules.
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// All logic dumped into the use case
|
|
429
|
+
class PlaceOrderUseCase {
|
|
430
|
+
execute(command) {
|
|
431
|
+
// Checking eligibility rules that belong to Order, Customer
|
|
432
|
+
if (command.customerStatus !== 'active') throw new Error();
|
|
433
|
+
if (command.items.length === 0) throw new Error();
|
|
434
|
+
// 80 lines of logic that should be on the entity
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**Why it happens:** Developers are used to ORM-style entities that map directly to tables. Business logic in entities feels "wrong" when entities also need to be serialized to JSON. The hexagonal layer structure is implemented, but the domain stays data-centric.
|
|
440
|
+
|
|
441
|
+
**Why it matters:** An anemic domain model removes the primary reason to use hexagonal architecture. The domain tests become trivial (no behavior to test). The use cases become bloated services. The architecture has the cost of hexagonal with none of the testability benefit.
|
|
442
|
+
|
|
443
|
+
**The fix:** Domain entities protect their invariants. An `Order` should know whether it can be placed, cancelled, shipped. An entity that can be in an invalid state by constructing it and calling setters directly is not a domain entity.
|
|
444
|
+
|
|
445
|
+
### FM-02: Interface Explosion
|
|
446
|
+
|
|
447
|
+
**What it looks like:** Every class has a matching interface, regardless of whether it will ever have a second implementation.
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
interface IOrderService { placeOrder(...): Promise<...>; }
|
|
451
|
+
class OrderServiceImpl implements IOrderService { ... }
|
|
452
|
+
|
|
453
|
+
interface IEmailService { send(...): Promise<...>; }
|
|
454
|
+
class EmailServiceImpl implements IEmailService { ... }
|
|
455
|
+
|
|
456
|
+
// OrderService has one implementation and will never have another.
|
|
457
|
+
// The interface adds no value, only indirection.
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**Why it happens:** Developers apply "every class needs an interface" as a blanket rule derived from misread Clean Architecture guidance.
|
|
461
|
+
|
|
462
|
+
**Why it matters:** Java/C# interfaces and TypeScript interfaces are not free — they add cognitive overhead, navigation indirection, and maintenance cost (renaming means updating two files). Interfaces earn their place when they enable polymorphism: multiple implementations, test doubles, or dependency inversion across a meaningful boundary.
|
|
463
|
+
|
|
464
|
+
**The rule:** Interfaces belong at architectural boundaries (the ports in hexagonal architecture). The `OrderRepository` interface is a port — it will have a `PostgresOrderRepository` and an `InMemoryOrderRepository` (for tests). The `PlaceOrderUseCase` interface exists because it enables the HTTP controller to depend on the abstraction, not the implementation, and because it can be replaced with a test double.
|
|
465
|
+
|
|
466
|
+
### FM-03: Use Case Per HTTP Endpoint
|
|
467
|
+
|
|
468
|
+
**What it looks like:** One use case class per REST endpoint, including trivial GET operations.
|
|
469
|
+
|
|
470
|
+
```
|
|
471
|
+
use-cases/
|
|
472
|
+
get-order-by-id.usecase.ts ← just delegates to repo.findById
|
|
473
|
+
get-all-orders.usecase.ts ← just delegates to repo.findAll
|
|
474
|
+
get-orders-by-customer.usecase.ts ← just delegates to repo.findByCustomerId
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
**Why it happens:** Teams apply the "one use case per operation" rule without distinguishing read operations (queries) that contain no business logic from command operations that do.
|
|
478
|
+
|
|
479
|
+
**Why it matters:** Trivial use cases that add no logic and no protection of invariants are indirection overhead. The HTTP controller calls the use case, which calls the repository, which executes the query. Three hops for no business value.
|
|
480
|
+
|
|
481
|
+
**The fix:** Apply CQRS thinking: commands (state-changing operations with business rules) benefit from use case encapsulation. Queries can often go directly from a query handler/controller to a read model or query repository without a use case intermediary.
|
|
482
|
+
|
|
483
|
+
### FM-04: DTO Hell
|
|
484
|
+
|
|
485
|
+
**What it looks like:** Objects are mapped between different representations at every layer crossing with no meaningful transformation happening.
|
|
486
|
+
|
|
487
|
+
```
|
|
488
|
+
HTTP Request JSON
|
|
489
|
+
→ RequestDTO (parsed by controller)
|
|
490
|
+
→ CommandDTO (passed to use case)
|
|
491
|
+
→ DomainEntity (created by use case)
|
|
492
|
+
→ PersistenceModel (mapped by repository)
|
|
493
|
+
→ DatabaseRow (stored)
|
|
494
|
+
→ PersistenceModel (retrieved)
|
|
495
|
+
→ DomainEntity (reconstructed)
|
|
496
|
+
→ ResponseDTO (created by use case)
|
|
497
|
+
→ HTTP Response JSON (serialized by controller)
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
If the `RequestDTO`, `CommandDTO`, and `ResponseDTO` all have the same fields and no transformation logic, this is not architecture — it is boilerplate accumulation.
|
|
501
|
+
|
|
502
|
+
**The fix:** DTOs earn their place at real semantic boundaries: the HTTP boundary (request/response shapes change independently from domain), the persistence boundary (ORM models and domain entities have different lifecycle concerns). If the fields and shapes are identical across all representations, consolidate.
|
|
503
|
+
|
|
504
|
+
### FM-05: Infrastructure Leaking Into the Domain
|
|
505
|
+
|
|
506
|
+
**What it looks like:** ORM annotations, framework decorators, or database column names appear in domain entities.
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
// Domain entity polluted with JPA/TypeORM annotations
|
|
510
|
+
@Entity('orders')
|
|
511
|
+
@Table({ name: 'orders' })
|
|
512
|
+
class Order {
|
|
513
|
+
@PrimaryGeneratedColumn('uuid')
|
|
514
|
+
@Column({ name: 'order_id' })
|
|
515
|
+
id: string;
|
|
516
|
+
|
|
517
|
+
@Column({ name: 'status_code', type: 'varchar' })
|
|
518
|
+
status: OrderStatus;
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
**Why it happens:** ORMs make it convenient to annotate domain entities directly. The first implementation of hexagonal architecture often uses this approach because it reduces the number of files. It works, until the ORM changes.
|
|
522
|
+
|
|
523
|
+
**Why it matters:** The domain entity now has a compile-time dependency on the ORM framework. The domain cannot be tested without the ORM being present on the classpath. The annotation-free domain entity has become annotation-full, violating the Dependency Rule.
|
|
524
|
+
|
|
525
|
+
**The fix:** Maintain a separate `OrderPersistenceModel` (or ORM entity) in the infrastructure layer. The `PostgresOrderRepository` maps between the domain `Order` and the `OrderPersistenceModel`. This doubles the persistence code but keeps the domain clean.
|
|
526
|
+
|
|
527
|
+
### FM-06: Circular Dependency Through Shared DTOs
|
|
528
|
+
|
|
529
|
+
**What it looks like:** The infrastructure layer imports from the presentation layer, or the domain imports from the application layer, because shared DTO types are defined in the wrong place.
|
|
530
|
+
|
|
531
|
+
**Why it happens:** Teams put command/response DTOs in a `shared/` folder that all layers import from, creating a dependency mesh.
|
|
532
|
+
|
|
533
|
+
**The fix:** DTOs travel inward with the request. Commands are defined in the application layer. Responses are defined in the application layer. Presentation maps from HTTP-specific structures to commands. Infrastructure maps from domain to persistence models.
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## Technology Landscape
|
|
538
|
+
|
|
539
|
+
### Node.js / TypeScript
|
|
540
|
+
|
|
541
|
+
No standard framework enforces hexagonal architecture. Teams construct it manually or use a dependency injection container.
|
|
542
|
+
|
|
543
|
+
- **DI Containers:** `tsyringe` (Microsoft), `inversify`, `awilix` — all support constructor injection for wiring adapters to ports at application startup.
|
|
544
|
+
- **NestJS:** Has a module system that can be used for hexagonal organization, but its decorators encourage infrastructure concerns in domain classes. Use carefully.
|
|
545
|
+
- **No-framework approach:** Explicit wiring in a `composition-root.ts` or `main.ts` is the cleanest approach and the most explicit.
|
|
546
|
+
- **ArchUnit equivalent:** `dependency-cruiser` can enforce import rules via configuration (no imports from `domain/` to `infrastructure/`).
|
|
547
|
+
|
|
548
|
+
### Java / Spring Boot
|
|
549
|
+
|
|
550
|
+
Spring Boot is well-suited for hexagonal architecture. Constructor injection is idiomatic. Spring does not force framework annotations into domain classes when used carefully.
|
|
551
|
+
|
|
552
|
+
- **Spring Dependency Injection:** Works naturally with port interfaces — Spring resolves the correct adapter at startup.
|
|
553
|
+
- **Spring Data:** Repository interfaces align naturally with hexagonal ports. The implementation can be the Spring Data adapter.
|
|
554
|
+
- **ArchUnit:** The Java library for enforcing architecture rules in tests. Can assert that `domain` packages have no dependencies on `infrastructure` packages. Should be standard in any hexagonal Java project.
|
|
555
|
+
- **Caution:** JPA/Hibernate annotations on domain entities are a common leakage point. Prefer separate persistence models.
|
|
556
|
+
|
|
557
|
+
### .NET
|
|
558
|
+
|
|
559
|
+
.NET has excellent built-in DI and multiple Clean Architecture templates:
|
|
560
|
+
|
|
561
|
+
- **Jason Taylor's Clean Architecture template** (GitHub: `jasontaylordev/CleanArchitecture`): Widely used .NET Clean Architecture scaffold with Application, Domain, Infrastructure, and WebUI projects.
|
|
562
|
+
- **Ardalis Clean Architecture** (GitHub: `ardalis/CleanArchitecture`): Steve Smith's variation, well-documented.
|
|
563
|
+
- **NetArchTest:** .NET equivalent of ArchUnit for enforcing dependency rules in tests.
|
|
564
|
+
- **MediatR:** Often used to implement the use case / CQRS pattern in .NET clean architecture. Commands and queries dispatched through a mediator.
|
|
565
|
+
|
|
566
|
+
### Python
|
|
567
|
+
|
|
568
|
+
No standard framework. Manual layering is common with FastAPI or Django:
|
|
569
|
+
|
|
570
|
+
- **FastAPI:** Dependency injection support makes constructor injection feasible. Routers as driving adapters, repository classes as driven adapters.
|
|
571
|
+
- **Django:** Its ORM is strongly coupled to models — keeping domain entities separate from Django models requires discipline and separate classes.
|
|
572
|
+
- **inject / dependency-injector:** Python DI containers for wiring adapters.
|
|
573
|
+
|
|
574
|
+
### Go
|
|
575
|
+
|
|
576
|
+
Go's package system and interface design naturally support hexagonal architecture:
|
|
577
|
+
|
|
578
|
+
- Go interfaces are implicit (structural typing) — any struct implementing the interface satisfies it without declaration.
|
|
579
|
+
- Repository interfaces are defined in the domain package. Implementations are in an `infrastructure/postgres/` or `infrastructure/memory/` package.
|
|
580
|
+
- No annotations exist, so infrastructure concerns cannot accidentally annotate domain structs.
|
|
581
|
+
- Go's explicit wiring in `main.go` forces a visible composition root.
|
|
582
|
+
|
|
583
|
+
### Enforcement Tooling
|
|
584
|
+
|
|
585
|
+
| Tool | Language | Purpose |
|
|
586
|
+
|---|---|---|
|
|
587
|
+
| ArchUnit | Java | Enforce import rules in JUnit tests |
|
|
588
|
+
| NetArchTest | .NET (C#) | Enforce import rules in xUnit/NUnit tests |
|
|
589
|
+
| dependency-cruiser | JS/TS | Static analysis of import graph, configurable rules |
|
|
590
|
+
| Import-linter | Python | Enforce import contracts in Python packages |
|
|
591
|
+
| go-arch-lint | Go | Enforce package dependency rules |
|
|
592
|
+
|
|
593
|
+
Enforcement tooling transforms the Dependency Rule from a convention into a compile-time (or test-time) constraint. Any team serious about hexagonal architecture should add this to CI.
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
597
|
+
## Decision Tree
|
|
598
|
+
|
|
599
|
+
Work through these in order. Stop at the first disqualifying condition.
|
|
600
|
+
|
|
601
|
+
```
|
|
602
|
+
1. Project life expectancy < 18 months?
|
|
603
|
+
YES → Use simple layered architecture. Skip hexagonal.
|
|
604
|
+
NO → Continue.
|
|
605
|
+
|
|
606
|
+
2. Fewer than 10 entities and no genuine business rules
|
|
607
|
+
(just CRUD + validation)?
|
|
608
|
+
YES → Use simple layered architecture. Skip hexagonal.
|
|
609
|
+
NO → Continue.
|
|
610
|
+
|
|
611
|
+
3. Is this a prototype, MVP, or short-lived admin tool?
|
|
612
|
+
YES → Skip hexagonal. Build the simplest thing that works.
|
|
613
|
+
NO → Continue.
|
|
614
|
+
|
|
615
|
+
4. Can you name 3+ business rules that change independently
|
|
616
|
+
from infrastructure/schema changes?
|
|
617
|
+
NO → Your domain is likely CRUD. Skip hexagonal.
|
|
618
|
+
YES → Continue.
|
|
619
|
+
|
|
620
|
+
5. Team < 3 developers AND < 12 month delivery horizon?
|
|
621
|
+
YES → Hexagonal overhead is not worth it. Use layered.
|
|
622
|
+
NO → Continue.
|
|
623
|
+
|
|
624
|
+
6. Do you need to test domain logic without spinning up a DB
|
|
625
|
+
or external services?
|
|
626
|
+
NO → Layered architecture with careful import discipline
|
|
627
|
+
may suffice.
|
|
628
|
+
YES → Strong signal for hexagonal.
|
|
629
|
+
|
|
630
|
+
7. Is infrastructure replacement realistic in the project
|
|
631
|
+
lifetime (ORM migration, DB swap, new delivery mechanism)?
|
|
632
|
+
YES → Hexagonal pays off clearly.
|
|
633
|
+
NO → Weigh testability benefit alone.
|
|
634
|
+
|
|
635
|
+
8. Does the domain have complex logic tested in complete
|
|
636
|
+
isolation, AND the system is expected to live 3+ years?
|
|
637
|
+
YES → Use hexagonal architecture. The investment is justified.
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
## Implementation Sketch
|
|
643
|
+
|
|
644
|
+
### Recommended Folder Structure
|
|
645
|
+
|
|
646
|
+
```
|
|
647
|
+
src/
|
|
648
|
+
domain/
|
|
649
|
+
orders/
|
|
650
|
+
order.entity.ts ← pure business logic, no imports
|
|
651
|
+
order-item.value-object.ts
|
|
652
|
+
order-status.ts ← enum / value object
|
|
653
|
+
money.value-object.ts
|
|
654
|
+
order.repository.ts ← port interface (driven)
|
|
655
|
+
payment-gateway.port.ts ← port interface (driven)
|
|
656
|
+
order-placed.event.ts ← domain event
|
|
657
|
+
customers/
|
|
658
|
+
customer.entity.ts
|
|
659
|
+
customer.repository.ts ← port interface (driven)
|
|
660
|
+
application/
|
|
661
|
+
use-cases/
|
|
662
|
+
place-order/
|
|
663
|
+
place-order.usecase.ts ← interface (driving port)
|
|
664
|
+
place-order.command.ts ← input DTO
|
|
665
|
+
place-order.result.ts ← output DTO
|
|
666
|
+
place-order.impl.ts ← implementation
|
|
667
|
+
cancel-order/
|
|
668
|
+
...
|
|
669
|
+
ports/
|
|
670
|
+
event-publisher.port.ts ← port interface (driven)
|
|
671
|
+
notification-service.port.ts ← port interface (driven)
|
|
672
|
+
infrastructure/
|
|
673
|
+
persistence/
|
|
674
|
+
postgres/
|
|
675
|
+
order.persistence-model.ts ← ORM entity (separate from domain)
|
|
676
|
+
postgres-order.repository.ts
|
|
677
|
+
memory/
|
|
678
|
+
in-memory-order.repository.ts ← test double / dev adapter
|
|
679
|
+
payment/
|
|
680
|
+
stripe/
|
|
681
|
+
stripe-payment-gateway.ts
|
|
682
|
+
notification/
|
|
683
|
+
sendgrid/
|
|
684
|
+
sendgrid-notification-service.ts
|
|
685
|
+
presentation/
|
|
686
|
+
http/
|
|
687
|
+
orders/
|
|
688
|
+
order.controller.ts ← driving adapter
|
|
689
|
+
order.request.dto.ts
|
|
690
|
+
order.response.dto.ts
|
|
691
|
+
grpc/
|
|
692
|
+
orders/
|
|
693
|
+
order.grpc-handler.ts ← driving adapter
|
|
694
|
+
scheduled/
|
|
695
|
+
order-expiry.job.ts ← driving adapter
|
|
696
|
+
composition-root.ts ← wires adapters to ports
|
|
697
|
+
main.ts
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
### Domain Entity Example
|
|
701
|
+
|
|
702
|
+
```typescript
|
|
703
|
+
// src/domain/orders/order.entity.ts
|
|
704
|
+
// Zero imports from infrastructure, frameworks, or ORMs
|
|
705
|
+
|
|
706
|
+
import { OrderItem } from './order-item.value-object';
|
|
707
|
+
import { OrderStatus } from './order-status';
|
|
708
|
+
import { Money } from './money.value-object';
|
|
709
|
+
import { OrderPlacedEvent } from './order-placed.event';
|
|
710
|
+
import { Customer } from '../customers/customer.entity';
|
|
711
|
+
|
|
712
|
+
export class Order {
|
|
713
|
+
private _domainEvents: unknown[] = [];
|
|
714
|
+
|
|
715
|
+
private constructor(
|
|
716
|
+
public readonly id: string,
|
|
717
|
+
public readonly customerId: string,
|
|
718
|
+
public readonly items: OrderItem[],
|
|
719
|
+
public readonly totalAmount: Money,
|
|
720
|
+
private _status: OrderStatus,
|
|
721
|
+
public readonly placedAt: Date
|
|
722
|
+
) {}
|
|
723
|
+
|
|
724
|
+
static place(customer: Customer, items: OrderItem[]): Order {
|
|
725
|
+
if (!customer.isActive()) {
|
|
726
|
+
throw new Error('Cannot place order for inactive customer');
|
|
727
|
+
}
|
|
728
|
+
if (items.length === 0) {
|
|
729
|
+
throw new Error('Order must contain at least one item');
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const total = items.reduce(
|
|
733
|
+
(sum, item) => sum.add(item.lineTotal),
|
|
734
|
+
Money.zero('USD')
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
const order = new Order(
|
|
738
|
+
generateId(),
|
|
739
|
+
customer.id,
|
|
740
|
+
items,
|
|
741
|
+
total,
|
|
742
|
+
OrderStatus.Pending,
|
|
743
|
+
new Date()
|
|
744
|
+
);
|
|
745
|
+
|
|
746
|
+
order._domainEvents.push(new OrderPlacedEvent(order.id, customer.id, total));
|
|
747
|
+
return order;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
cancel(): void {
|
|
751
|
+
if (this._status === OrderStatus.Shipped) {
|
|
752
|
+
throw new Error('Cannot cancel a shipped order');
|
|
753
|
+
}
|
|
754
|
+
if (this._status === OrderStatus.Cancelled) {
|
|
755
|
+
throw new Error('Order is already cancelled');
|
|
756
|
+
}
|
|
757
|
+
this._status = OrderStatus.Cancelled;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
confirm(): void {
|
|
761
|
+
if (this._status !== OrderStatus.Pending) {
|
|
762
|
+
throw new Error('Only pending orders can be confirmed');
|
|
763
|
+
}
|
|
764
|
+
this._status = OrderStatus.Confirmed;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
get status(): OrderStatus { return this._status; }
|
|
768
|
+
get domainEvents(): unknown[] { return [...this._domainEvents]; }
|
|
769
|
+
clearDomainEvents(): void { this._domainEvents = []; }
|
|
770
|
+
}
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
### Port Interface Example
|
|
774
|
+
|
|
775
|
+
```typescript
|
|
776
|
+
// src/domain/orders/order.repository.ts
|
|
777
|
+
// This is the driven port — defined in domain, implemented in infrastructure
|
|
778
|
+
|
|
779
|
+
import { Order } from './order.entity';
|
|
780
|
+
import { OrderId } from './order-status';
|
|
781
|
+
|
|
782
|
+
export interface OrderRepository {
|
|
783
|
+
findById(id: string): Promise<Order | null>;
|
|
784
|
+
findByCustomerId(customerId: string): Promise<Order[]>;
|
|
785
|
+
save(order: Order): Promise<void>;
|
|
786
|
+
delete(id: string): Promise<void>;
|
|
787
|
+
}
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
### Infrastructure Adapter Example
|
|
791
|
+
|
|
792
|
+
```typescript
|
|
793
|
+
// src/infrastructure/persistence/postgres/postgres-order.repository.ts
|
|
794
|
+
// This adapter implements the port — only this file knows about TypeORM
|
|
795
|
+
|
|
796
|
+
import { Repository } from 'typeorm';
|
|
797
|
+
import { OrderRepository } from '../../../domain/orders/order.repository';
|
|
798
|
+
import { Order } from '../../../domain/orders/order.entity';
|
|
799
|
+
import { OrderPersistenceModel } from './order.persistence-model';
|
|
800
|
+
import { OrderMapper } from './order.mapper';
|
|
801
|
+
|
|
802
|
+
export class PostgresOrderRepository implements OrderRepository {
|
|
803
|
+
constructor(private readonly repo: Repository<OrderPersistenceModel>) {}
|
|
804
|
+
|
|
805
|
+
async findById(id: string): Promise<Order | null> {
|
|
806
|
+
const model = await this.repo.findOne({ where: { id } });
|
|
807
|
+
return model ? OrderMapper.toDomain(model) : null;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
async save(order: Order): Promise<void> {
|
|
811
|
+
const model = OrderMapper.toPersistence(order);
|
|
812
|
+
await this.repo.save(model);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
async findByCustomerId(customerId: string): Promise<Order[]> {
|
|
816
|
+
const models = await this.repo.find({ where: { customerId } });
|
|
817
|
+
return models.map(OrderMapper.toDomain);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
async delete(id: string): Promise<void> {
|
|
821
|
+
await this.repo.delete({ id });
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
### Composition Root
|
|
827
|
+
|
|
828
|
+
```typescript
|
|
829
|
+
// src/composition-root.ts
|
|
830
|
+
// This is the only place that knows about all adapters
|
|
831
|
+
|
|
832
|
+
import { DataSource } from 'typeorm';
|
|
833
|
+
import { PostgresOrderRepository } from './infrastructure/persistence/postgres/postgres-order.repository';
|
|
834
|
+
import { StripePaymentGateway } from './infrastructure/payment/stripe/stripe-payment-gateway';
|
|
835
|
+
import { KafkaEventPublisher } from './infrastructure/messaging/kafka/kafka-event-publisher';
|
|
836
|
+
import { PlaceOrderUseCaseImpl } from './application/use-cases/place-order/place-order.impl';
|
|
837
|
+
|
|
838
|
+
export function buildContainer(dataSource: DataSource) {
|
|
839
|
+
// Wire adapters to ports
|
|
840
|
+
const orderRepository = new PostgresOrderRepository(
|
|
841
|
+
dataSource.getRepository(OrderPersistenceModel)
|
|
842
|
+
);
|
|
843
|
+
const paymentGateway = new StripePaymentGateway(process.env.STRIPE_KEY!);
|
|
844
|
+
const eventPublisher = new KafkaEventPublisher(process.env.KAFKA_BROKERS!);
|
|
845
|
+
|
|
846
|
+
// Wire use cases to their dependencies (all interfaces)
|
|
847
|
+
const placeOrderUseCase = new PlaceOrderUseCaseImpl(
|
|
848
|
+
orderRepository,
|
|
849
|
+
paymentGateway,
|
|
850
|
+
eventPublisher
|
|
851
|
+
);
|
|
852
|
+
|
|
853
|
+
return { placeOrderUseCase };
|
|
854
|
+
}
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
---
|
|
858
|
+
|
|
859
|
+
## Cross-References
|
|
860
|
+
|
|
861
|
+
- **layered-architecture** — The architecture most teams start from before earning hexagonal. Understanding its pain points (DB-coupled unit tests, framework lock-in) motivates the transition.
|
|
862
|
+
- **domain-driven-design** — DDD provides the vocabulary and modeling techniques for the domain layer: entities, value objects, aggregates, domain events, bounded contexts. Hexagonal architecture and DDD complement each other naturally.
|
|
863
|
+
- **separation-of-concerns** — The foundational principle underlying the Dependency Rule. Each ring has a single concern; no ring knows the internal concerns of another.
|
|
864
|
+
- **coupling-and-cohesion** — Ports and adapters are the mechanism for achieving low coupling between domain and infrastructure while maintaining high cohesion within each layer.
|
|
865
|
+
- **design-principles-solid** — The Dependency Inversion Principle (D in SOLID) is the direct technical mechanism of hexagonal architecture: high-level modules (domain) do not depend on low-level modules (infrastructure); both depend on abstractions (ports).
|
|
866
|
+
- **modular-monolith** — Hexagonal architecture is frequently used as the internal structure of modules within a modular monolith. Each module has its own hexagonal structure; inter-module communication is through defined interfaces.
|
|
867
|
+
|
|
868
|
+
---
|
|
869
|
+
|
|
870
|
+
*Researched: 2026-03-08 | Sources: [Alistair Cockburn, "Hexagonal Architecture" (alistair.cockburn.us)](https://alistair.cockburn.us/hexagonal-architecture) · [AWS Prescriptive Guidance — Hexagonal Architecture Pattern](https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/hexagonal-architecture.html) · [Three Dots Labs — Is Clean Architecture Overengineering?](https://threedots.tech/episode/is-clean-architecture-overengineering/) · [DEV Community — Stop Overengineering in the Name of Clean Architecture](https://dev.to/criscmd/stop-overengineering-in-the-name-of-clean-architecture-b8h) · [devblog.tech — The Clean Architecture Trap](https://devblog.tech/the-clean-architecture-trap-why-simplicity-beats-overengineering-in-net/) · [DEV Community — Why I Can't Recommend Clean Architecture by Robert C. Martin](https://dev.to/bosepchuk/why-i-cant-recommend-clean-architecture-by-robert-c-martin-ofd) · [HappyCoders.eu — Hexagonal Architecture: What Is It? Why Use It?](https://www.happycoders.eu/software-craftsmanship/hexagonal-architecture/) · [Philippe Bourgau — Avoid Mocks and Test Your Core Domain Faster](https://philippe.bourgau.net/avoid-mocks-and-test-your-core-domain-faster-with-hexagonal-architecture/) · [Medium — Understanding Hexagonal, Clean, Onion, and Layered Architectures](https://romanglushach.medium.com/understanding-hexagonal-clean-onion-and-traditional-layered-architectures-a-deep-dive-c0f93b8a1b96) · [CCD Akademie — Clean Architecture vs. Onion Architecture vs. Hexagonal Architecture](https://ccd-akademie.de/en/clean-architecture-vs-onion-architecture-vs-hexagonal-architecture/) · [Martin Fowler — Anemic Domain Model](https://martinfowler.com/bliki/AnemicDomainModel.html)*
|