В этом варианте есть:
- Entity + Aggregate Root:
Order(id =OrderId) - Entity внутри агрегата:
OrderItem(id =OrderItemId) - Value Objects:
Money,OrderId,OrderItemId - Domain Service:
DiscountPolicy(валидирует/разрешает скидочные коды) - Domain Events
- Repository (in-memory)
- Application Command Handlers + Query Service
- Factory
- Specification
- Второй bounded context (
pricing) + ACL между контекстами
src/ordering/domain/*- чистая доменная модель (aggregate, entities, value objects, domain services, domain shared)src/ordering/application/*- orchestration/use-cases, commands, contracts и interfacessrc/ordering/infrastructure/*- адаптеры (in-memory repository, random id generator, ACL)src/pricing/*- отдельный bounded context для промо-правилsrc/index.ts- composition root
docs/ddd/evans-concepts-map.md- где именно в коде лежат понятия из Evans DDDdocs/ddd/evans-terminology-glossary.md- словарь терминов Evans DDD и когда какой термин применятьdocs/ddd/context-map.md- связи bounded contexts
npm i
npm run devПосле запуска npm run dev в начале вывода будет блок:
[BC DEMO] Same discount, different bounded contexts
[BC DEMO] Pricing language: { couponCode: 'SAVE10', discountRate: 0.1 }
[BC DEMO] Ordering language via ACL: { code: 'SAVE10', percent: 10 }
Это показывает границу контекстов:
pricingговорит на языкеcouponCode+discountRateorderingпринимает толькоcode+percent- перевод делается в ACL (
src/ordering/infrastructure/acl/pricing.discount.policy.acl.ts)