@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,795 @@
|
|
|
1
|
+
# Third-Party Integration -- Architecture Expertise Module
|
|
2
|
+
|
|
3
|
+
> Third-party integration covers the architecture of connecting your system to external services
|
|
4
|
+
> (payment processors, email providers, cloud APIs, SaaS platforms). The key principle: never let
|
|
5
|
+
> a third-party API shape your domain model. Use adapter/anti-corruption layers to isolate external
|
|
6
|
+
> dependencies so that vendor changes, outages, and API deprecations remain contained at the boundary.
|
|
7
|
+
|
|
8
|
+
> **Category:** Integration
|
|
9
|
+
> **Complexity:** Moderate
|
|
10
|
+
> **Applies when:** Integrating with any external service -- payment processing, email/SMS, cloud storage, analytics, authentication providers, AI/ML APIs, or any vendor whose availability and API surface you do not control.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## What This Is (and What It Isn't)
|
|
15
|
+
|
|
16
|
+
### Core Concepts
|
|
17
|
+
|
|
18
|
+
**Adapter Pattern for External Services.** An adapter wraps a third-party API behind an interface
|
|
19
|
+
that your domain defines. Your domain declares what it needs (a port): "I need to charge a
|
|
20
|
+
customer." The adapter translates that into Stripe's `PaymentIntent.create()` call, or Braintree's
|
|
21
|
+
`Transaction.sale()`, or Adyen's `/payments` endpoint. The domain never imports the vendor SDK.
|
|
22
|
+
The adapter lives in the infrastructure layer and implements the domain's port.
|
|
23
|
+
|
|
24
|
+
**Anti-Corruption Layer (ACL).** Coined by Eric Evans in *Domain-Driven Design* (2003), the ACL
|
|
25
|
+
is a boundary that prevents an external system's model from leaking into your domain. The external
|
|
26
|
+
system has its own language, its own entity shapes, its own invariants. The ACL translates between
|
|
27
|
+
the two models. Without it, your domain objects accumulate fields like `stripe_customer_id`,
|
|
28
|
+
`twilio_message_sid`, `sendgrid_template_id` -- vendor concepts that have no place in your
|
|
29
|
+
business logic. The ACL is more than an adapter: it actively reshapes data, maps foreign concepts
|
|
30
|
+
to domain concepts, and may aggregate or decompose entities to maintain domain integrity.
|
|
31
|
+
|
|
32
|
+
**Facade for External APIs.** When an external service exposes a sprawling API surface (AWS S3
|
|
33
|
+
has 100+ operations, Stripe has 300+ endpoints), a facade narrows the interface to only the
|
|
34
|
+
operations your system actually uses. This reduces the blast radius of API changes and makes the
|
|
35
|
+
integration testable. If you use 4 of Stripe's 300 endpoints, your facade exposes 4 methods.
|
|
36
|
+
|
|
37
|
+
**Vendor Abstraction.** A generalized interface that lets you swap between vendors without
|
|
38
|
+
touching application code. Your system defines a `NotificationSender` interface; one adapter
|
|
39
|
+
sends via Twilio SMS, another via AWS SNS, another via Firebase Cloud Messaging. The application
|
|
40
|
+
code calls `notificationSender.send(recipient, message)` and does not know or care which vendor
|
|
41
|
+
fulfills it. This is the Dependency Inversion Principle applied to external service boundaries.
|
|
42
|
+
|
|
43
|
+
### What It Is NOT
|
|
44
|
+
|
|
45
|
+
**Not about wrapping every HTTP call.** If your system makes a one-off call to a public REST API
|
|
46
|
+
to fetch weather data for a display widget, you do not need a port, adapter, ACL, and factory.
|
|
47
|
+
A simple HTTP client call with error handling is sufficient. Architecture is about protecting
|
|
48
|
+
the parts of your system that matter, not about ceremony for its own sake.
|
|
49
|
+
|
|
50
|
+
**Not about eliminating all vendor awareness.** Some vendor-specific concerns are legitimate at
|
|
51
|
+
the infrastructure level. A Stripe webhook handler needs to verify Stripe's signature format.
|
|
52
|
+
An AWS S3 adapter needs to handle multipart uploads. The goal is not to pretend vendors don't
|
|
53
|
+
exist -- it is to confine vendor awareness to a specific architectural layer.
|
|
54
|
+
|
|
55
|
+
**Not a guarantee against vendor lock-in.** An adapter interface reduces switching cost, but it
|
|
56
|
+
does not eliminate it. If you depend on Stripe's dispute resolution workflow, subscription
|
|
57
|
+
lifecycle events, and billing portal, switching to Braintree means rebuilding all of those
|
|
58
|
+
adapter implementations. The abstraction makes the switch *possible* without domain changes;
|
|
59
|
+
it does not make the switch *cheap*.
|
|
60
|
+
|
|
61
|
+
**Not always the right investment.** Abstraction has a cost: indirection, maintenance burden,
|
|
62
|
+
and the risk of a leaky or lowest-common-denominator interface. The decision to abstract must
|
|
63
|
+
be justified by the likelihood and cost of change.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## When to Use Abstraction Layers
|
|
68
|
+
|
|
69
|
+
### The Qualifying Conditions
|
|
70
|
+
|
|
71
|
+
**Multiple vendors are realistic.** Your system may need to support Stripe in the US, Adyen
|
|
72
|
+
in Europe, and Razorpay in India. You genuinely need a `PaymentGateway` interface with multiple
|
|
73
|
+
implementations. This is the strongest justification for adapter/ACL investment.
|
|
74
|
+
|
|
75
|
+
**Vendor switching is a real business risk.** The vendor may raise prices, deprecate a critical
|
|
76
|
+
feature, suffer reliability issues, or lose compliance certifications. Shopify's architecture
|
|
77
|
+
decouples core commerce logic from payment gateways specifically because merchants routinely
|
|
78
|
+
switch between Stripe, PayPal, and local processors. The switching cost needs to be contained.
|
|
79
|
+
|
|
80
|
+
**The vendor's model conflicts with your domain model.** Stripe models payments as
|
|
81
|
+
`PaymentIntent` -> `Charge` -> `BalanceTransaction`. Your domain might model payments as
|
|
82
|
+
`Order` -> `Payment` -> `Settlement`. These are fundamentally different entity graphs. Without
|
|
83
|
+
an ACL, your domain classes inherit Stripe's terminology and lifecycle, making it impossible
|
|
84
|
+
to reason about your business in your own terms.
|
|
85
|
+
|
|
86
|
+
**The vendor API is unstable or rapidly evolving.** Stripe ships breaking changes in new API
|
|
87
|
+
versions roughly annually. OpenAI's API evolved from completions to chat completions to
|
|
88
|
+
assistants within two years. If the vendor moves fast, an adapter layer absorbs those changes
|
|
89
|
+
in one place rather than scattering updates across your codebase.
|
|
90
|
+
|
|
91
|
+
**You need to test business logic without the vendor.** Your checkout flow has 47 business
|
|
92
|
+
rules about discounts, taxes, shipping calculations, and inventory checks. If the payment
|
|
93
|
+
call is embedded directly in the checkout service, testing those 47 rules requires mocking
|
|
94
|
+
Stripe at the HTTP level. With an adapter, you inject a `FakePaymentGateway` that returns
|
|
95
|
+
success/failure deterministically.
|
|
96
|
+
|
|
97
|
+
**Regulatory or compliance boundaries.** PCI DSS requires that cardholder data handling is
|
|
98
|
+
isolated and auditable. GDPR requires that data sent to third parties is tracked. An adapter
|
|
99
|
+
layer provides a natural audit boundary: all data flowing to the vendor passes through a
|
|
100
|
+
single, reviewable chokepoint.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## When NOT to Use Abstraction Layers
|
|
105
|
+
|
|
106
|
+
This section is as important as the previous one. Over-abstraction of third-party integrations
|
|
107
|
+
is one of the most common architectural mistakes, producing interfaces that serve no purpose
|
|
108
|
+
except to satisfy a pattern fetish.
|
|
109
|
+
|
|
110
|
+
### The Disqualifying Conditions
|
|
111
|
+
|
|
112
|
+
**You will only ever use one vendor, and that is a deliberate strategic choice.** If your
|
|
113
|
+
entire business model is built on Shopify's platform, you are not going to switch to
|
|
114
|
+
WooCommerce. If you are an AWS shop that has committed to a three-year Reserved Instance
|
|
115
|
+
contract with enterprise support, abstracting S3 behind a `FileStorage` interface "in case
|
|
116
|
+
you switch to GCP" is a fantasy. The abstraction will rot because it is never exercised by
|
|
117
|
+
a second implementation.
|
|
118
|
+
|
|
119
|
+
A fintech startup spent four months building a vendor-agnostic payment abstraction layer
|
|
120
|
+
supporting Stripe, Braintree, and Adyen. They launched with Stripe. Three years later, they
|
|
121
|
+
still only use Stripe. The abstraction layer has been maintained across 14 Stripe API version
|
|
122
|
+
upgrades, each requiring changes to the "vendor-agnostic" interface because the interface was
|
|
123
|
+
unknowingly shaped by Stripe's model. The second and third adapters were never written.
|
|
124
|
+
|
|
125
|
+
**The abstraction costs more than switching would.** If switching vendors is a one-time,
|
|
126
|
+
two-week project that happens at most once in the system's lifetime, spending four months
|
|
127
|
+
building and maintaining an abstraction layer to make that switch take one week instead of
|
|
128
|
+
two is a net loss. Calculate: (cost of maintaining abstraction x system lifetime) vs.
|
|
129
|
+
(one-time cost of switching without abstraction). If the latter is smaller, skip the layer.
|
|
130
|
+
|
|
131
|
+
**Thin abstractions that just proxy calls.** An interface `EmailSender` with one method
|
|
132
|
+
`send(to, subject, body)` that has exactly one implementation `SendGridEmailSender` which
|
|
133
|
+
calls `sendGrid.send(to, subject, body)` is not an abstraction -- it is a forwarding layer.
|
|
134
|
+
It adds a file, an interface, and a binding without providing any value. If the interface
|
|
135
|
+
method signature is a 1:1 mirror of the vendor method, you have not abstracted anything.
|
|
136
|
+
You have added indirection.
|
|
137
|
+
|
|
138
|
+
**The vendor IS the product.** If you are building a Stripe dashboard, a Twilio call center,
|
|
139
|
+
or an AWS management console, the vendor API is not an external dependency -- it is your
|
|
140
|
+
domain. Abstracting it away means abstracting away your core business logic. A Stripe
|
|
141
|
+
analytics tool that hides Stripe's data model behind a "vendor-agnostic payment" interface
|
|
142
|
+
loses the ability to surface Stripe-specific insights that are the tool's entire value.
|
|
143
|
+
|
|
144
|
+
**Prototypes and MVPs.** You are validating whether customers want the feature at all. Do not
|
|
145
|
+
build adapter layers for a feature that might be deleted in six weeks. Call Stripe directly.
|
|
146
|
+
If the feature survives and the system grows, introduce the abstraction then. This is not
|
|
147
|
+
technical debt -- it is appropriate engineering for the stage of the product.
|
|
148
|
+
|
|
149
|
+
**The lowest-common-denominator problem.** If Stripe supports subscriptions with metered
|
|
150
|
+
billing, proration, and usage-based pricing, but Braintree only supports flat-rate
|
|
151
|
+
subscriptions, your "vendor-agnostic" subscription interface can only expose the intersection
|
|
152
|
+
of their capabilities: flat-rate subscriptions. You lose access to Stripe features that your
|
|
153
|
+
customers need, or you add vendor-specific escape hatches that defeat the purpose of the
|
|
154
|
+
abstraction. The richer vendor's unique capabilities leak through the interface or are
|
|
155
|
+
abandoned.
|
|
156
|
+
|
|
157
|
+
**Real example of over-abstraction.** A SaaS company built a `CloudProvider` interface
|
|
158
|
+
abstracting AWS, GCP, and Azure behind unified `compute()`, `store()`, and `queue()` methods.
|
|
159
|
+
The interface could not express AWS Lambda's cold start characteristics, GCP's BigQuery
|
|
160
|
+
integration, or Azure's Active Directory hooks. Every meaningful feature required
|
|
161
|
+
vendor-specific code paths. The abstraction became a lie -- callers needed to know which
|
|
162
|
+
vendor was behind the interface to use it correctly. The team eventually deleted the
|
|
163
|
+
abstraction and used vendor SDKs directly with thin, vendor-specific adapters per service.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## How It Works
|
|
168
|
+
|
|
169
|
+
### 1. Adapter Pattern Implementation
|
|
170
|
+
|
|
171
|
+
The adapter pattern for third-party services follows a structure:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
Domain Layer (your code):
|
|
175
|
+
defines PaymentGateway port (interface)
|
|
176
|
+
defines domain types: Money, PaymentResult, PaymentMethod
|
|
177
|
+
|
|
178
|
+
Infrastructure Layer (your code):
|
|
179
|
+
StripePaymentGateway implements PaymentGateway
|
|
180
|
+
- imports Stripe SDK
|
|
181
|
+
- translates domain types to/from Stripe types
|
|
182
|
+
- handles Stripe-specific errors, retries, idempotency keys
|
|
183
|
+
|
|
184
|
+
BraintreePaymentGateway implements PaymentGateway
|
|
185
|
+
- imports Braintree SDK
|
|
186
|
+
- translates domain types to/from Braintree types
|
|
187
|
+
- handles Braintree-specific error codes
|
|
188
|
+
|
|
189
|
+
Application Layer (your code):
|
|
190
|
+
CheckoutService depends on PaymentGateway (the interface)
|
|
191
|
+
- never imports Stripe or Braintree
|
|
192
|
+
- testable with FakePaymentGateway
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The critical discipline: the interface is defined in terms of your domain, not the vendor's.
|
|
196
|
+
`PaymentGateway.charge(Money amount, PaymentMethod method)` -- not
|
|
197
|
+
`PaymentGateway.createPaymentIntent(int amountInCents, String currency, String paymentMethodId)`.
|
|
198
|
+
The former is your language. The latter is Stripe's language wearing your interface's clothes.
|
|
199
|
+
|
|
200
|
+
### 2. Anti-Corruption Layer Translation
|
|
201
|
+
|
|
202
|
+
The ACL does more than route calls. It translates between two conceptual models.
|
|
203
|
+
|
|
204
|
+
**Example -- CRM Integration:**
|
|
205
|
+
|
|
206
|
+
Your domain has `Customer` with `loyaltyTier`, `lifetimeValue`, and `preferredContactMethod`.
|
|
207
|
+
Salesforce has `Contact` with `Account`, `Opportunity`, and 200 custom fields. The ACL:
|
|
208
|
+
|
|
209
|
+
- Maps `Customer` to a Salesforce `Contact` + `Account` (one domain entity becomes two Salesforce
|
|
210
|
+
entities).
|
|
211
|
+
- Translates `loyaltyTier` (an enum: BRONZE, SILVER, GOLD) to Salesforce's custom field
|
|
212
|
+
`Loyalty_Level__c` (a string: "B", "S", "G").
|
|
213
|
+
- Ignores the 180 Salesforce fields your system does not use.
|
|
214
|
+
- Handles Salesforce's governor limits (API calls per 24-hour window) internally.
|
|
215
|
+
- Converts Salesforce's `SystemModstamp` (ISO 8601 with timezone) to your domain's `updatedAt`
|
|
216
|
+
(UTC epoch milliseconds).
|
|
217
|
+
|
|
218
|
+
Without the ACL, Salesforce's model colonizes your codebase. With the ACL, your domain stays
|
|
219
|
+
clean and the Salesforce translation logic is contained in one package.
|
|
220
|
+
|
|
221
|
+
### 3. Circuit Breaker for External Calls
|
|
222
|
+
|
|
223
|
+
Third-party services fail. They fail more often than your own services because you do not
|
|
224
|
+
control their infrastructure, deployment schedule, or capacity planning. The circuit breaker
|
|
225
|
+
pattern prevents a failing third-party from cascading into your system.
|
|
226
|
+
|
|
227
|
+
**Three states:**
|
|
228
|
+
|
|
229
|
+
- **Closed (normal):** Requests flow to the third party. The breaker tracks failure rates.
|
|
230
|
+
- **Open (tripped):** After the failure threshold is exceeded (e.g., 50% of requests failed
|
|
231
|
+
in the last 30 seconds), the breaker opens. All requests are immediately rejected with a
|
|
232
|
+
fallback response. No calls reach the third party. This protects your system from hanging
|
|
233
|
+
on timeouts and protects the third party from retry storms.
|
|
234
|
+
- **Half-open (probing):** After a cooldown period (e.g., 60 seconds), the breaker allows
|
|
235
|
+
one probe request. If it succeeds, the breaker closes. If it fails, the breaker reopens.
|
|
236
|
+
|
|
237
|
+
**Real-world example -- Stripe latency outage (2022):** Increased traffic combined with an
|
|
238
|
+
unbalanced connection pool saturated a database cluster. Downstream clients without circuit
|
|
239
|
+
breakers continued retrying, amplifying the load. Services with circuit breakers detected
|
|
240
|
+
the elevated latency, opened their breakers, and served cached or fallback responses.
|
|
241
|
+
Services without breakers queued requests until their own thread pools exhausted, causing
|
|
242
|
+
cascading failures in their own systems.
|
|
243
|
+
|
|
244
|
+
### 4. Retry with Exponential Backoff
|
|
245
|
+
|
|
246
|
+
Not every failure is permanent. Network blips, rate limits, and transient 503 errors are
|
|
247
|
+
common with third-party APIs. Retry logic handles these, but naive retries cause more harm
|
|
248
|
+
than good.
|
|
249
|
+
|
|
250
|
+
**Rules for retry:**
|
|
251
|
+
|
|
252
|
+
- **Retry only idempotent operations** or operations with idempotency keys. Retrying a
|
|
253
|
+
non-idempotent `POST /charges` without an idempotency key risks double-charging.
|
|
254
|
+
- **Exponential backoff:** Wait 1s, 2s, 4s, 8s, 16s. Not 1s, 1s, 1s, 1s (which becomes
|
|
255
|
+
a retry storm under load).
|
|
256
|
+
- **Jitter:** Add random variation (0-500ms) to the backoff interval. Without jitter, when
|
|
257
|
+
a third-party recovers from an outage, all clients retry simultaneously at the same
|
|
258
|
+
exponential intervals, creating a thundering herd.
|
|
259
|
+
- **Maximum retries:** Cap at 3-5 attempts. Beyond that, the failure is not transient.
|
|
260
|
+
- **Retry only retriable errors:** Retry on 429, 503, 504, connection timeout. Do not retry
|
|
261
|
+
on 400 (bad request), 401 (auth failure), 404 (not found), or 422 (validation error).
|
|
262
|
+
These will never succeed on retry.
|
|
263
|
+
|
|
264
|
+
**Combine with circuit breaker:** Retries happen inside the circuit breaker. If the breaker
|
|
265
|
+
is open, retries are skipped entirely. This prevents retry storms from preventing the
|
|
266
|
+
breaker from recovering.
|
|
267
|
+
|
|
268
|
+
### 5. Timeout Configuration
|
|
269
|
+
|
|
270
|
+
Every external call must have a timeout. Without one, a hanging connection holds a thread
|
|
271
|
+
and potentially a database transaction open indefinitely.
|
|
272
|
+
|
|
273
|
+
- **Connection timeout (1-5s):** Time to establish TCP connection. Fail fast if unreachable.
|
|
274
|
+
- **Read timeout (5-30s):** Time to receive a response. Varies by operation (Stripe charge:
|
|
275
|
+
2-5s; AI inference: up to 30s).
|
|
276
|
+
- **Overall timeout (10-60s):** Total budget including retries.
|
|
277
|
+
|
|
278
|
+
**Do not use vendor SDK defaults.** Stripe's Ruby SDK defaulted to 80 seconds for years. One
|
|
279
|
+
slow call holding a thread for 80 seconds under load can exhaust your entire thread pool.
|
|
280
|
+
|
|
281
|
+
### 6. Credential Management
|
|
282
|
+
|
|
283
|
+
- **Never store credentials in code.** Use environment variables for simple deployments;
|
|
284
|
+
secrets managers (Vault, AWS Secrets Manager) for production.
|
|
285
|
+
- **Rotate on a schedule.** Create new key, deploy, verify, then revoke old key. Automate.
|
|
286
|
+
- **Scope minimally.** If you only create charges and read customers, use a restricted key.
|
|
287
|
+
- **Separate per environment.** Staging using production Stripe keys is a billing incident.
|
|
288
|
+
|
|
289
|
+
**Real incident -- Uber (2016):** AWS credentials committed to GitHub. Attackers accessed
|
|
290
|
+
57 million user records. Root cause: credential management, not an AWS vulnerability.
|
|
291
|
+
|
|
292
|
+
### 7. Webhook Handling
|
|
293
|
+
|
|
294
|
+
Webhooks are how third-party services notify you of events (payment completed, subscription
|
|
295
|
+
cancelled, email bounced). They invert the communication direction: instead of polling,
|
|
296
|
+
the vendor pushes to you.
|
|
297
|
+
|
|
298
|
+
**Architectural requirements:**
|
|
299
|
+
|
|
300
|
+
- **Verify signatures on every webhook.** Stripe signs payloads with HMAC-SHA256 using
|
|
301
|
+
your webhook signing secret. Verify the signature using the raw request body and a
|
|
302
|
+
timing-safe comparison. Without verification, anyone can POST fake events to your
|
|
303
|
+
webhook endpoint.
|
|
304
|
+
- **Enforce timestamp windows.** Reject webhooks with timestamps older than 5-10 minutes
|
|
305
|
+
to prevent replay attacks.
|
|
306
|
+
- **Implement idempotency.** Webhooks are delivered at least once, not exactly once.
|
|
307
|
+
Store processed event IDs (e.g., Stripe's `evt_xxxxx`) in your database or Redis with
|
|
308
|
+
a TTL of 7-30 days. Before processing, check if the event ID exists. If it does, return
|
|
309
|
+
200 and skip processing. This prevents duplicate order fulfillment, duplicate emails,
|
|
310
|
+
and duplicate inventory decrements.
|
|
311
|
+
- **Respond fast, process asynchronously.** Return HTTP 200 within 1-3 seconds. Enqueue
|
|
312
|
+
the event for background processing. If your webhook handler takes 30 seconds to process
|
|
313
|
+
an event synchronously, the vendor will time out and retry, causing duplicate delivery.
|
|
314
|
+
Treat webhook receivers as verify-enqueue-acknowledge services.
|
|
315
|
+
- **Handle out-of-order delivery.** Webhooks may arrive out of order. A `payment.succeeded`
|
|
316
|
+
event might arrive before the `payment.created` event. Design your handler to be
|
|
317
|
+
order-independent, or implement ordering logic using event timestamps and sequence numbers.
|
|
318
|
+
- **Set up a dead-letter queue.** Events that fail processing after all retries should be
|
|
319
|
+
stored in a DLQ for manual inspection and replay, not silently dropped.
|
|
320
|
+
|
|
321
|
+
### 8. Rate Limit Handling
|
|
322
|
+
|
|
323
|
+
Third-party APIs enforce rate limits (Stripe: 100 reads/s live, 25 test; Twilio:
|
|
324
|
+
per-account concurrency; SendGrid: plan-tier based).
|
|
325
|
+
|
|
326
|
+
- **Read rate limit headers.** Track `X-RateLimit-Remaining` and `Retry-After` to throttle
|
|
327
|
+
proactively before hitting the limit.
|
|
328
|
+
- **Client-side rate limiting.** Token bucket or leaky bucket in your adapter prevents
|
|
329
|
+
bursts from triggering 429 responses.
|
|
330
|
+
- **Queue and batch.** SendGrid supports 1,000 recipients per call. Batch where possible.
|
|
331
|
+
- **Degrade gracefully on 429.** Surface as delays ("processing, ready in a few minutes"),
|
|
332
|
+
not errors ("service unavailable"). Back off using `Retry-After`.
|
|
333
|
+
|
|
334
|
+
### 9. SDK vs. Direct HTTP
|
|
335
|
+
|
|
336
|
+
**Use the SDK when:** it handles authentication, pagination, webhook verification, and
|
|
337
|
+
versioning correctly; it provides type safety; your adapter wraps it so the rest of your
|
|
338
|
+
system never sees SDK types.
|
|
339
|
+
|
|
340
|
+
**Use direct HTTP when:** the SDK is unmaintained, pulls heavy transitive dependencies,
|
|
341
|
+
you only use 2-3 endpoints, or cold start time matters in serverless environments.
|
|
342
|
+
|
|
343
|
+
**The hybrid approach:** Use the SDK inside your adapter, but define the adapter interface
|
|
344
|
+
in domain terms. `charge(amount: Money): PaymentResult` calls `stripe.paymentIntents.create()`
|
|
345
|
+
internally. If you switch to direct HTTP, only the adapter body changes.
|
|
346
|
+
|
|
347
|
+
### 10. Error Mapping
|
|
348
|
+
|
|
349
|
+
Third-party error responses must be translated into your domain's error model. Do not let
|
|
350
|
+
vendor error codes propagate into your application layer.
|
|
351
|
+
|
|
352
|
+
- **Map vendor errors to domain errors.** Stripe's `card_declined` becomes your
|
|
353
|
+
`PaymentDeclined`. Your application catches domain exceptions, not Stripe exceptions.
|
|
354
|
+
- **Log vendor diagnostics, never expose them.** The adapter logs `Stripe: card_declined
|
|
355
|
+
(req_xxx)` and throws `PaymentDeclined` with a user-safe message.
|
|
356
|
+
- **Distinguish retriable from terminal.** `rate_limit` is retriable (retry may succeed).
|
|
357
|
+
`card_declined` is terminal (do not retry). Your error model must express this.
|
|
358
|
+
|
|
359
|
+
### 11. Health Checks for External Dependencies
|
|
360
|
+
|
|
361
|
+
- **Shallow check:** Verify reachability (DNS, TCP, TLS). Catches network-level failures.
|
|
362
|
+
- **Deep check:** Lightweight read-only API call (Stripe: `GET /v1/balance`). Catches auth
|
|
363
|
+
failures and account suspension.
|
|
364
|
+
- **Never block your primary health check on third-party health.** Stripe down means
|
|
365
|
+
degraded, not dead. Report vendor status on a dependency endpoint. If your liveness probe
|
|
366
|
+
fails because Stripe is down, Kubernetes restarts healthy pods unnecessarily.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Trade-Offs Matrix
|
|
371
|
+
|
|
372
|
+
| Decision | Benefit | Cost | When to Accept the Cost |
|
|
373
|
+
|----------|---------|------|------------------------|
|
|
374
|
+
| Full adapter + ACL layer | Vendor-independent domain; clean testability | More code, more indirection; risk of lowest-common-denominator interface | Multiple vendors realistic; domain model is valuable and long-lived |
|
|
375
|
+
| Direct vendor SDK usage | Fastest integration; access to all vendor features | Domain polluted with vendor types; switching cost is high | Single vendor commitment; prototype/MVP; vendor IS the domain |
|
|
376
|
+
| Circuit breaker | Prevents cascading failure from vendor outage | Added complexity; risk of opening too aggressively (false positives) | Any vendor where downtime causes user-facing impact |
|
|
377
|
+
| Retry with backoff | Handles transient failures transparently | Increased latency on failure paths; retry storms if misconfigured | All integrations, but only for idempotent or idempotency-keyed operations |
|
|
378
|
+
| Webhook-based integration | Real-time updates; no polling overhead | Must handle at-least-once delivery, out-of-order events, signature verification | Vendor supports webhooks and you need real-time event processing |
|
|
379
|
+
| Polling-based integration | Simpler implementation; you control the schedule | Higher latency; wasted API calls on no-change polls; rate limit consumption | Vendor does not support webhooks; event freshness requirements are relaxed |
|
|
380
|
+
| SDK usage | Faster development; handles auth, pagination, versioning | Dependency on vendor's SDK quality and maintenance; transitive dependency risk | SDK is well-maintained, actively versioned, and does not conflict with your stack |
|
|
381
|
+
| Direct HTTP calls | Full control; minimal dependencies; smaller bundle | Must implement auth, pagination, error handling, versioning manually | SDK is unavailable, unmaintained, or adds excessive dependency weight |
|
|
382
|
+
| Client-side rate limiting | Prevents 429 errors; smoother request distribution | Adds state management (token bucket); may under-utilize available quota | High-volume integrations where hitting rate limits causes user-facing degradation |
|
|
383
|
+
| Multi-vendor fallback | Resilience against single vendor outage | Maintaining two vendor integrations; data consistency between vendors | The integration is mission-critical and single-vendor outage is unacceptable |
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Evolution Path
|
|
388
|
+
|
|
389
|
+
### Stage 1: Direct Integration (Day 1 - MVP)
|
|
390
|
+
|
|
391
|
+
Call the vendor SDK directly from your service layer. No adapter, no interface, no ACL.
|
|
392
|
+
Ship the feature and validate that customers want it. Vendor types may appear in your
|
|
393
|
+
service signatures. This is acceptable at this stage.
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
CheckoutService -> stripe.paymentIntents.create(...)
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**Cost of staying here too long:** Vendor concepts spread throughout the codebase. Tests
|
|
400
|
+
require mocking the vendor at the HTTP level. Switching vendors means touching every file
|
|
401
|
+
that imports the SDK.
|
|
402
|
+
|
|
403
|
+
### Stage 2: Extract Adapter (Month 3-6)
|
|
404
|
+
|
|
405
|
+
When the integration stabilizes and the feature is validated, extract the vendor calls into
|
|
406
|
+
a dedicated adapter class. Define an interface in your domain layer. Inject the adapter.
|
|
407
|
+
Write tests against a fake implementation.
|
|
408
|
+
|
|
409
|
+
```
|
|
410
|
+
CheckoutService -> PaymentGateway (interface) -> StripePaymentGateway (adapter)
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
**Trigger:** You need to write tests for checkout logic without calling Stripe. Or you
|
|
414
|
+
receive the first request to support a second payment provider.
|
|
415
|
+
|
|
416
|
+
### Stage 3: Add Resilience (Month 6-12)
|
|
417
|
+
|
|
418
|
+
Wrap the adapter with circuit breaker, retry, and timeout logic. Implement health checks.
|
|
419
|
+
Add monitoring for vendor API latency and error rates. Implement webhook handling with
|
|
420
|
+
idempotency and signature verification.
|
|
421
|
+
|
|
422
|
+
**Trigger:** The first vendor outage that causes user-facing impact. Or your system reaches
|
|
423
|
+
scale where transient errors are no longer rare edge cases.
|
|
424
|
+
|
|
425
|
+
### Stage 4: Multi-Vendor Support (Year 1-2)
|
|
426
|
+
|
|
427
|
+
If a second vendor is genuinely needed, implement a second adapter behind the same interface.
|
|
428
|
+
Add a routing layer that selects the vendor based on geography, feature requirements, or
|
|
429
|
+
failover rules. Refine the interface to accommodate both vendors without becoming a
|
|
430
|
+
lowest-common-denominator contract.
|
|
431
|
+
|
|
432
|
+
**Trigger:** Business expansion to a region where the current vendor does not operate. Or
|
|
433
|
+
contract negotiation where having a second vendor provides leverage.
|
|
434
|
+
|
|
435
|
+
### Stage 5: Platform Abstraction (Year 2+)
|
|
436
|
+
|
|
437
|
+
For systems with many integrations (10+ vendors), extract the integration infrastructure
|
|
438
|
+
into a shared platform: circuit breaker configuration, credential management, rate limiting,
|
|
439
|
+
health checking, and monitoring. Individual adapters focus only on translation logic.
|
|
440
|
+
|
|
441
|
+
**Trigger:** You are maintaining 10+ integrations and each one reimplements retry, timeout,
|
|
442
|
+
and error mapping logic independently.
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## Failure Modes
|
|
447
|
+
|
|
448
|
+
### 1. Vendor Outage Cascading to Your System
|
|
449
|
+
|
|
450
|
+
**What happens:** Stripe goes down. Your checkout service calls Stripe with a 60-second
|
|
451
|
+
timeout. All checkout requests hang for 60 seconds. Your web server's thread pool is
|
|
452
|
+
exhausted. Your health check times out. Kubernetes restarts the pod. The new pod immediately
|
|
453
|
+
fills its thread pool with hanging Stripe calls. Your entire system is down because one
|
|
454
|
+
vendor is down.
|
|
455
|
+
|
|
456
|
+
**Real incident -- Slack (2020):** A massive user disconnection-reconnection event exceeded
|
|
457
|
+
database capacity, causing cascading connection failures. 5% of users could not connect for
|
|
458
|
+
over 2 hours. Services that depended on Slack's API for notifications experienced silent
|
|
459
|
+
failures.
|
|
460
|
+
|
|
461
|
+
**Real incident -- Cloudflare (November 2025):** A Cloudflare outage took down Resend's
|
|
462
|
+
email delivery service from 11:30 UTC to 14:31 UTC. Every service that depended on Resend
|
|
463
|
+
for transactional email lost email delivery capability for three hours.
|
|
464
|
+
|
|
465
|
+
**Prevention:** Circuit breakers with aggressive timeouts (5s, not 60s). Fallback responses
|
|
466
|
+
for non-critical paths. Graceful degradation: if Stripe is down, show "Payment processing
|
|
467
|
+
is temporarily delayed" instead of a 500 error. Queue the payment attempt for retry.
|
|
468
|
+
|
|
469
|
+
### 2. Rate Limiting Causing User-Facing Errors
|
|
470
|
+
|
|
471
|
+
**What happens:** A batch job runs during peak hours and consumes your entire Stripe rate
|
|
472
|
+
limit. Real-time checkout requests receive 429 responses. Users see "Payment failed."
|
|
473
|
+
|
|
474
|
+
**Prevention:** Separate API keys for batch and real-time operations (most vendors support
|
|
475
|
+
this). Client-side rate limiting with priority queues: real-time operations get priority
|
|
476
|
+
over batch operations. Schedule batch jobs during off-peak hours.
|
|
477
|
+
|
|
478
|
+
### 3. Credential Rotation Failures
|
|
479
|
+
|
|
480
|
+
**What happens:** Your API key rotation script creates a new key, deploys it to staging,
|
|
481
|
+
but fails to deploy to production. The old key is revoked. Production loses access to the
|
|
482
|
+
vendor API.
|
|
483
|
+
|
|
484
|
+
**Real incident -- Redocly (January 2026):** A background job queue error triggered a
|
|
485
|
+
cascading failure that, among other things, exhausted the secrets engine. With the secrets
|
|
486
|
+
engine rejecting requests, the orchestration layer could not start new API allocations,
|
|
487
|
+
leaving the API in a flapping state.
|
|
488
|
+
|
|
489
|
+
**Prevention:** Blue-green credential rotation: deploy the new credential, verify it
|
|
490
|
+
works in production with a health check, then revoke the old credential. Never revoke
|
|
491
|
+
before verifying. Keep the old credential valid for a grace period (24-48 hours).
|
|
492
|
+
|
|
493
|
+
### 4. API Version Deprecation Surprises
|
|
494
|
+
|
|
495
|
+
**What happens:** Stripe deprecates API version `2022-11-15` with 12 months notice. Your
|
|
496
|
+
team misses the deprecation email. Twelve months later, API calls start returning errors
|
|
497
|
+
with unknown fields or changed response shapes.
|
|
498
|
+
|
|
499
|
+
**Prevention:** Pin your API version explicitly (in the SDK configuration, not implicitly).
|
|
500
|
+
Monitor vendor changelogs and deprecation notices. Subscribe to vendor status pages and
|
|
501
|
+
engineering blogs. Include API version in your health check response so monitoring can
|
|
502
|
+
alert on upcoming deprecations.
|
|
503
|
+
|
|
504
|
+
### 5. Webhook Delivery Failures
|
|
505
|
+
|
|
506
|
+
**What happens:** Your webhook endpoint is down for 4 hours. The vendor retries with
|
|
507
|
+
exponential backoff. When your endpoint comes back, you receive 4 hours of webhooks in
|
|
508
|
+
rapid succession. Your processing pipeline is overwhelmed. Some events are processed out
|
|
509
|
+
of order. A subscription cancellation event arrives before the subscription creation event.
|
|
510
|
+
|
|
511
|
+
**Prevention:** Asynchronous processing with a queue between the webhook receiver and the
|
|
512
|
+
processor. Idempotency checks on event IDs. Order-independent event handling or explicit
|
|
513
|
+
ordering logic using event timestamps. Auto-scaling the processor based on queue depth.
|
|
514
|
+
|
|
515
|
+
### 6. Silent Data Corruption from API Changes
|
|
516
|
+
|
|
517
|
+
**What happens:** A vendor changes a field from string to integer, or adds a required field,
|
|
518
|
+
or changes the semantics of an existing field. Your deserialization succeeds (the SDK handles
|
|
519
|
+
the type change) but your business logic makes incorrect decisions based on the changed
|
|
520
|
+
semantics.
|
|
521
|
+
|
|
522
|
+
**Real example:** A vendor changed a `status` field from `"active"` to `"enabled"`. The
|
|
523
|
+
adapter checked `status === "active"`, which now always returned false. All users appeared
|
|
524
|
+
inactive. No error was thrown. The issue was discovered three days later by a customer
|
|
525
|
+
support ticket.
|
|
526
|
+
|
|
527
|
+
**Prevention:** Contract testing. Use tools like Pact or vendor-provided contract test
|
|
528
|
+
suites. Assert on the shape and semantics of vendor responses in your integration tests.
|
|
529
|
+
Monitor business metrics (conversion rates, active user counts) for anomalies that may
|
|
530
|
+
indicate silent API changes.
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## Technology Landscape
|
|
535
|
+
|
|
536
|
+
### Common Integrations and Their Characteristics
|
|
537
|
+
|
|
538
|
+
| Service | Category | SDK Quality | Rate Limits | Webhook Support | API Stability |
|
|
539
|
+
|---------|----------|-------------|-------------|-----------------|---------------|
|
|
540
|
+
| **Stripe** | Payments | Excellent SDKs in 7+ languages; strongly typed | 100 reads/s live, 25 test | Comprehensive; signed; retries with backoff | Annual versions; 12-month deprecation |
|
|
541
|
+
| **Twilio** | SMS/Voice | Good SDKs; well-documented | Per-account concurrency | Webhook-first design; status callbacks | Stable; long deprecation cycles |
|
|
542
|
+
| **SendGrid** | Email | Good SDKs; supports batch | Plan-tier based | Event webhooks for delivery, opens, clicks | Stable |
|
|
543
|
+
| **AWS (S3/SQS/SNS)** | Cloud infra | Official SDKs; auto-retry built in | Service-specific; generous | SNS for notifications; EventBridge for events | Backwards-compatible; never removes APIs |
|
|
544
|
+
| **Auth0** | Authentication | Good SDKs; Management + Auth APIs | Rate limits by plan and endpoint | Hooks and Actions (server-side); Log Streams | Stable |
|
|
545
|
+
| **OpenAI** | AI/ML | SDKs in Python/Node; evolving rapidly | Tokens/min + requests/min by model | No webhooks; polling for async jobs | Rapidly evolving; frequent breaking changes |
|
|
546
|
+
| **Salesforce** | CRM | Heavy SDK; SOAP and REST APIs | API call limits per 24-hour window | Outbound Messages; Platform Events | Versioned; long support cycles |
|
|
547
|
+
|
|
548
|
+
### SDK vs. REST Decision by Vendor
|
|
549
|
+
|
|
550
|
+
**Use the SDK:** Stripe (handles idempotency keys, pagination, webhook verification),
|
|
551
|
+
AWS (handles SigV4 signing, retry, pagination), Auth0 (handles token management, JWKS caching).
|
|
552
|
+
|
|
553
|
+
**Consider direct HTTP:** Simple REST APIs with API key auth (SendGrid for basic sends),
|
|
554
|
+
vendors with outdated/unmaintained SDKs, serverless functions where cold start matters,
|
|
555
|
+
GraphQL APIs where the SDK adds no value over a generic GraphQL client.
|
|
556
|
+
|
|
557
|
+
### API Gateway for External Calls
|
|
558
|
+
|
|
559
|
+
Some architectures route all outbound third-party calls through an API gateway or proxy:
|
|
560
|
+
|
|
561
|
+
- **Benefits:** Centralized logging, rate limiting, circuit breaking, credential injection,
|
|
562
|
+
and audit trail. Single place to enforce timeout policies.
|
|
563
|
+
- **Costs:** Additional network hop (latency), single point of failure, operational complexity.
|
|
564
|
+
- **When it makes sense:** Regulated industries (finance, healthcare) where all external
|
|
565
|
+
data flow must be audited. Systems with 10+ integrations where per-adapter resilience
|
|
566
|
+
configuration is unsustainable.
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## Decision Tree
|
|
571
|
+
|
|
572
|
+
```
|
|
573
|
+
START: You need to integrate with a third-party service.
|
|
574
|
+
|
|
575
|
+
Q1: Is this a prototype/MVP or a production system?
|
|
576
|
+
-> Prototype/MVP: Call the vendor SDK/API directly. No adapter.
|
|
577
|
+
Skip to: "Add error handling and timeouts."
|
|
578
|
+
-> Production: Continue.
|
|
579
|
+
|
|
580
|
+
Q2: Will you realistically use more than one vendor for this capability?
|
|
581
|
+
-> Yes (e.g., payment in multiple regions): Build adapter + ACL.
|
|
582
|
+
-> No, but switching is possible in 2+ years: Build a thin adapter.
|
|
583
|
+
Interface matches your domain. One implementation. Easy to add a second later.
|
|
584
|
+
-> No, and the vendor IS the product: Call the vendor SDK directly.
|
|
585
|
+
No adapter. Vendor types in your service layer are acceptable.
|
|
586
|
+
|
|
587
|
+
Q3: Does the vendor's data model conflict with your domain model?
|
|
588
|
+
-> Yes (different entity shapes, terminology, lifecycle): Build an ACL.
|
|
589
|
+
Translate at the boundary. Domain never sees vendor types.
|
|
590
|
+
-> No (vendor model aligns with your domain): Adapter is sufficient.
|
|
591
|
+
Translation is minimal (type conversion, error mapping).
|
|
592
|
+
|
|
593
|
+
Q4: Is the vendor call in a critical user-facing path?
|
|
594
|
+
-> Yes (checkout, login, real-time messaging):
|
|
595
|
+
Add circuit breaker + retry with backoff + aggressive timeout.
|
|
596
|
+
Add fallback behavior (cached response, graceful degradation).
|
|
597
|
+
-> No (batch processing, async analytics):
|
|
598
|
+
Add retry with backoff + timeout. Circuit breaker optional.
|
|
599
|
+
|
|
600
|
+
Q5: Does the vendor push events to you (webhooks)?
|
|
601
|
+
-> Yes: Implement signature verification + idempotency + async processing.
|
|
602
|
+
-> No, but you need real-time updates: Implement polling with backoff.
|
|
603
|
+
-> No, and batch freshness is fine: Scheduled sync job.
|
|
604
|
+
|
|
605
|
+
Q6: SDK or direct HTTP?
|
|
606
|
+
-> SDK if: well-maintained, handles auth/pagination/versioning, your adapter wraps it.
|
|
607
|
+
-> HTTP if: SDK unavailable/bloated, you use <5 endpoints, cold start matters.
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
---
|
|
611
|
+
|
|
612
|
+
## Implementation Sketch
|
|
613
|
+
|
|
614
|
+
### Adapter Interface (Domain Layer)
|
|
615
|
+
|
|
616
|
+
```typescript
|
|
617
|
+
// domain/ports/PaymentGateway.ts
|
|
618
|
+
// This interface is defined by YOUR domain, not by Stripe.
|
|
619
|
+
|
|
620
|
+
interface PaymentGateway {
|
|
621
|
+
charge(params: ChargeParams): Promise<ChargeResult>;
|
|
622
|
+
refund(paymentId: PaymentId, amount: Money): Promise<RefundResult>;
|
|
623
|
+
getPaymentStatus(paymentId: PaymentId): Promise<PaymentStatus>;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
interface ChargeParams {
|
|
627
|
+
orderId: OrderId; // YOUR domain's order identifier
|
|
628
|
+
amount: Money; // YOUR domain's money type (amount + currency)
|
|
629
|
+
method: PaymentMethodRef; // YOUR domain's reference to a stored payment method
|
|
630
|
+
idempotencyKey: string; // Caller-generated, prevents double charges
|
|
631
|
+
metadata?: Record<string, string>;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
interface ChargeResult {
|
|
635
|
+
paymentId: PaymentId; // YOUR domain's payment identifier
|
|
636
|
+
status: PaymentStatus; // YOUR enum: SUCCEEDED | PENDING | FAILED
|
|
637
|
+
failureReason?: PaymentFailureReason; // YOUR enum: DECLINED | INSUFFICIENT_FUNDS | ...
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Note: no Stripe types, no stripe-specific fields, no vendor terminology.
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### Concrete Stripe Adapter (Infrastructure Layer)
|
|
644
|
+
|
|
645
|
+
```typescript
|
|
646
|
+
// infrastructure/payment/StripePaymentGateway.ts
|
|
647
|
+
|
|
648
|
+
class StripePaymentGateway implements PaymentGateway {
|
|
649
|
+
private stripe: Stripe;
|
|
650
|
+
|
|
651
|
+
constructor(apiKey: string, apiVersion: string = '2024-11-20') {
|
|
652
|
+
this.stripe = new Stripe(apiKey, { apiVersion, timeout: 10_000 });
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
async charge(params: ChargeParams): Promise<ChargeResult> {
|
|
656
|
+
try {
|
|
657
|
+
const intent = await this.stripe.paymentIntents.create(
|
|
658
|
+
{
|
|
659
|
+
amount: params.amount.toSmallestUnit(), // $10.50 -> 1050 cents
|
|
660
|
+
currency: params.amount.currency.toLowerCase(),
|
|
661
|
+
payment_method: params.method.vendorRef,
|
|
662
|
+
confirm: true,
|
|
663
|
+
metadata: { order_id: params.orderId.toString(), ...params.metadata },
|
|
664
|
+
},
|
|
665
|
+
{ idempotencyKey: params.idempotencyKey }
|
|
666
|
+
);
|
|
667
|
+
return {
|
|
668
|
+
paymentId: PaymentId.from(intent.id),
|
|
669
|
+
status: this.mapStatus(intent.status),
|
|
670
|
+
};
|
|
671
|
+
} catch (error) {
|
|
672
|
+
if (error instanceof Stripe.errors.StripeCardError) {
|
|
673
|
+
return {
|
|
674
|
+
paymentId: PaymentId.generate(),
|
|
675
|
+
status: PaymentStatus.FAILED,
|
|
676
|
+
failureReason: this.mapDeclineCode(error.decline_code),
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
if (error instanceof Stripe.errors.StripeRateLimitError) {
|
|
680
|
+
throw new ServiceTemporarilyUnavailable('Payment service rate limited');
|
|
681
|
+
}
|
|
682
|
+
throw new PaymentServiceError('Unexpected payment error', { cause: error });
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Maps Stripe status strings to domain PaymentStatus enum
|
|
687
|
+
private mapStatus(s: string): PaymentStatus {
|
|
688
|
+
if (s === 'succeeded') return PaymentStatus.SUCCEEDED;
|
|
689
|
+
if (s === 'processing' || s === 'requires_action') return PaymentStatus.PENDING;
|
|
690
|
+
return PaymentStatus.FAILED;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// Maps Stripe decline codes to domain PaymentFailureReason enum
|
|
694
|
+
private mapDeclineCode(code?: string): PaymentFailureReason {
|
|
695
|
+
if (code === 'insufficient_funds') return PaymentFailureReason.INSUFFICIENT_FUNDS;
|
|
696
|
+
if (code === 'lost_card' || code === 'stolen_card') return PaymentFailureReason.CARD_INVALID;
|
|
697
|
+
if (code === 'expired_card') return PaymentFailureReason.CARD_EXPIRED;
|
|
698
|
+
return PaymentFailureReason.DECLINED;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### Fallback Provider Pattern
|
|
704
|
+
|
|
705
|
+
```typescript
|
|
706
|
+
// infrastructure/payment/FallbackPaymentGateway.ts
|
|
707
|
+
|
|
708
|
+
class FallbackPaymentGateway implements PaymentGateway {
|
|
709
|
+
constructor(
|
|
710
|
+
private primary: PaymentGateway,
|
|
711
|
+
private fallback: PaymentGateway,
|
|
712
|
+
private circuitBreaker: CircuitBreaker,
|
|
713
|
+
private logger: Logger
|
|
714
|
+
) {}
|
|
715
|
+
|
|
716
|
+
async charge(params: ChargeParams): Promise<ChargeResult> {
|
|
717
|
+
try {
|
|
718
|
+
return await this.circuitBreaker.execute(() => this.primary.charge(params));
|
|
719
|
+
} catch (error) {
|
|
720
|
+
if (error instanceof ServiceTemporarilyUnavailable ||
|
|
721
|
+
error instanceof CircuitBreakerOpen ||
|
|
722
|
+
error instanceof TimeoutError) {
|
|
723
|
+
this.logger.warn('Primary gateway failed, falling back', { orderId: params.orderId });
|
|
724
|
+
return this.fallback.charge(params);
|
|
725
|
+
}
|
|
726
|
+
throw error; // Terminal errors (card declined) are not retried on fallback
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Composition at bootstrap -- CheckoutService has no idea which vendor is behind the interface
|
|
732
|
+
const paymentGateway = new FallbackPaymentGateway(
|
|
733
|
+
new StripePaymentGateway(config.stripeKey),
|
|
734
|
+
new BraintreePaymentGateway(config.braintreeKey),
|
|
735
|
+
new CircuitBreaker({ failureThreshold: 5, resetTimeout: 30_000 }),
|
|
736
|
+
logger
|
|
737
|
+
);
|
|
738
|
+
const checkoutService = new CheckoutService(paymentGateway, orderRepo);
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### Webhook Handler with Idempotency
|
|
742
|
+
|
|
743
|
+
```typescript
|
|
744
|
+
// infrastructure/webhooks/StripeWebhookHandler.ts
|
|
745
|
+
|
|
746
|
+
class StripeWebhookHandler {
|
|
747
|
+
constructor(
|
|
748
|
+
private webhookSecret: string,
|
|
749
|
+
private eventStore: ProcessedEventStore, // Redis or DB
|
|
750
|
+
private eventQueue: EventQueue, // SQS, RabbitMQ, etc.
|
|
751
|
+
) {}
|
|
752
|
+
|
|
753
|
+
async handle(rawBody: Buffer, signatureHeader: string): Promise<void> {
|
|
754
|
+
// 1. Verify signature (timing-safe comparison via Stripe SDK)
|
|
755
|
+
const event = stripe.webhooks.constructEvent(rawBody, signatureHeader, this.webhookSecret);
|
|
756
|
+
|
|
757
|
+
// 2. Idempotency check -- skip already-processed events
|
|
758
|
+
if (await this.eventStore.hasBeenProcessed(event.id)) return;
|
|
759
|
+
|
|
760
|
+
// 3. Enqueue for async processing, ACK fast to Stripe
|
|
761
|
+
await this.eventQueue.enqueue({ eventId: event.id, type: event.type, data: event.data.object });
|
|
762
|
+
await this.eventStore.markReceived(event.id);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
---
|
|
768
|
+
|
|
769
|
+
## Cross-References
|
|
770
|
+
|
|
771
|
+
- **[hexagonal-clean-architecture](../patterns/hexagonal-clean-architecture.md)** --
|
|
772
|
+
Third-party adapters are the canonical example of driven adapters (secondary ports) in
|
|
773
|
+
hexagonal architecture. The payment gateway port is a driven port; the Stripe adapter is
|
|
774
|
+
a driven adapter. If you are building a system with hexagonal architecture, third-party
|
|
775
|
+
integrations are where the pattern pays its biggest dividends.
|
|
776
|
+
|
|
777
|
+
- **[coupling-and-cohesion](../foundations/coupling-and-cohesion.md)** -- Third-party
|
|
778
|
+
integrations without adapter layers create stamp coupling (your domain shares data
|
|
779
|
+
structures with the vendor) and content coupling (your domain depends on the vendor's
|
|
780
|
+
internal model). The adapter pattern converts this to data coupling (your domain
|
|
781
|
+
communicates with the adapter through well-defined domain parameters).
|
|
782
|
+
|
|
783
|
+
- **circuit-breaker-bulkhead** -- The circuit breaker and bulkhead patterns are essential
|
|
784
|
+
for any third-party integration in a critical path. Circuit breakers prevent vendor
|
|
785
|
+
outages from cascading. Bulkheads isolate vendor failures so that a Stripe outage does
|
|
786
|
+
not consume the thread pool needed for serving cached content.
|
|
787
|
+
|
|
788
|
+
- **api-design-rest** -- When your system itself exposes APIs that wrap third-party
|
|
789
|
+
capabilities (e.g., a unified payments API), the API design principles for REST apply.
|
|
790
|
+
Your API should expose your domain model, not the vendor's model.
|
|
791
|
+
|
|
792
|
+
- **webhooks-and-callbacks** -- Webhook handling is a deep topic on its own: signature
|
|
793
|
+
verification, idempotency, ordering, dead-letter queues, and replay mechanisms. The
|
|
794
|
+
webhook section in this module covers the third-party integration perspective; the
|
|
795
|
+
dedicated module covers the full architectural pattern.
|