Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

zhuravlevma/typescript-ddd-architecture

Repository files navigation

DDD patterns with examples

Marketplace Domain

Event Storming schema

image

Describe

Bounded Contexts:

  • Cart - Context for cart operations
    • Subdomains: -
      • Core Cart - adding items to the shopping cart
      • Generic Payment - payment for goods
  • Warehouse - Context for warehouse operations
    • Subdomains: -
      • Core OrderManagement - order management at the warehouse
      • Supporting Location - management of product locations at the warehouse, product categorization
  • Accounting - accounting context
    • Subdomains: -
      • Core Reports - financial reports generation
      • Supporting Verification - order verification
  • Delivery - delivery context
    • Subdomains: -
      • Core Board - board of order proposals
      • Core Couriers - management of couriers
      • Supporting Tracking - delivery status tracking

Module boundaries

This project is a large monolith structured at a high level into bounded contexts. Each context contains subdomains that, depending on the type, implement their architectural pattern. For the Core subdomain, a Domain model is chosen, while for the Supporting subdomain, either Transaction script or Active Record is implemented as its architectural pattern.

If you have a large monolith that contains many bounded contexts, then the service can be divided into modules by context.

If you have a micro service architecture and you prefer to allocate contexts to different services (which is preferable). If it's not enough for you, then you can also divide subdomains into services. Each Core subdomain can be divided into modules by aggregates.

Why do I need an event bus?

Firstly, we have a limitation - this is the change of one aggregate in one transaction (strong consistency). To change multiple aggregates at the same time, you need to use eventual consistency.

Why do I need Relay?

We cannot write a message directly to the broker, because it may not be available. Pattern Transactional outbox.

Transactional outbox can be done using synchronous calls, the broker is not biased. But this option is more suitable for point-to-point communication.

In a good way, each bounded context in a micro-service architecture should have its own Relay. In the demonstration monolith, I decided to limit myself to one.

Saga

The project uses a saga with choreography and a registrar. The registrar is capable of rolling back transactions in case of failure.

Calling for compensation

image

Timeout compensation

image

Important

This is not a production ready solution!

ArchUnit

Utilities for convenient architectural testing (similar to ArchUnit in Java) have been developed for the project. They can be executed using the command:

npm run test:arch

Installation

npm install

Running the app (local)

CREATE SCHEMA IF NOT EXISTS accounting
CREATE SCHEMA IF NOT EXISTS warehouse
CREATE SCHEMA IF NOT EXISTS delivery
CREATE SCHEMA IF NOT EXISTS public
cp .env.example .env
npm install
npm install dotenv-cli
dotenv npm run start:dev

Running the app (docker)

cp .env.example .env
docker-compose up

Test

# unit tests
$ npm run test
# arch tests
$ npm run test:arch
# test coverage
$ npm run test:cov

Contributors 2

Languages

AltStyle によって変換されたページ (->オリジナル) /