Skip to content

dr-marek-jaskula/DomainDrivenDesignUniversity

Repository files navigation

Domain Driven Design University 🏫

This project presents the domain driven design concepts.

Domain and Application projects are structured using vertical slices/screaming architecture/by aggregates. Nevertheless, for Infrastructure, Persistence, Presentation projects, I have decided to create a folder structure based on respective types. From my perspective it is a preferred approach, because these projects do not have many aggregate-related structures. Thus, organizing them in screaming architecture would result in less readable and less maintainable solution.

Give a Star! ⭐

If you like or are using this project to learn about Domain Driven Design, please give it a star. Thanks!

Solution structure πŸ”

The solution in designed using the Clean Architecture in the following form.

cleanest-architecture

I prefer to present it like this instead of using other Clean Architecture images, because in my opinion this is more readable. The runner layer is required so that the Presentation, Infrastructure and Persistence layers can be truly separated.

.App πŸš—

This layer should register dependencies and run the program. In terms of presented architecture schema it is a runner.

.Presentation πŸšͺ

This layer should provide endpoints for the end user.

Therefore, it is like a "frontend" for the backed program.

.Application πŸ’»

This layer should provide handlers that orchestrate domain objects to obtain business features.

Therefore, we place here middlewares, CQRS structure and mappings.

.Infrastructure 🏭

This layer should provide additional tools (not database specific) that we are going to use.

Therefore, we place here services, options, validators, adapters, providers, policies and so on.

.Persistence πŸ“š

This layer should provide database specific structures, configurations, repositories, specifications, background jobs.

.Domain βš“

This is the heart of the whole solution. We store here entities, value objects, enumerations, domain events and other core structures.

.Tests.Unit βœ”οΈ

Project to present structure of unit tests.

It contains also domain tests, architecture tests and utility tests.

.Tests.Integration βœ”οΈ

This layer contains integrations tests.

Here we do not use the WebApplicationFactory approach, but we create DI container in more manual way.

Moreover, tests are done on the test database, but the test cleaning is required (see TestDataGeneratorBase.CleanDatabaseFromTestData).

Note: use these tests without containerized application and for the database that is locally installed.

.Tests.Integration.Container βœ”οΈ

This layer contains integrations tests.

Here we use the proper approach of using the WebApplicationFactory.

Moreover, we use TestContainers library, to make all integrations tests in sql database inside the container, so the additional database cleaning is not required (like in Shopway.Tests.Integration).

.Tests.Performance βœ”οΈ

This layer contains performance tests.

We use NBomber as a performance test framework, because we can use it with xunit.

Note: use these tests without containerized application and for the database that is locally installed.

.Tests Naming Conventions πŸ“œ

  1. Tests should follow T1_T2_T3 convention where:
    • T1 is the name of the system functionality that is under test
    • T2 is the expected result
    • T3 is the test scenario

GetById_ShouldReturnProduct_WhenProductExists

Note: Alternative naming convention: switch T2 with T3:

GetById_WhenProductExists_ShouldReturnProduct

  1. Mocks should end with "Mock" suffix:

_productRepositoryMock

Run Application with Local Database ▢️

  1. Select Shopway.App as startup project.
  2. Create databases called DDDUniveristy and DDDUniversityTest.
  3. Run the application

Run Containerized Application with Containerized Database and Custom Payment Gateway 🐳

Note: Portainer is used to investigate containers if required.

  1. Select docker-compose as startup project.
  2. Run the application

Alternatively:

  1. Open console at the solution level ..\DomainDrivenDesignUniversity>
  2. Run in detached mode:
docker compose -f docker-compose.yml -f docker-compose.override.yml up -d

To stop containers use:

docker compose -f docker-compose.yml -f docker-compose.override.yml down

If image rebuild is required:

docker compose -f .\docker-compose.yml -f .\docker-compose.override.yml up --build

Payment Gateway πŸ’°

Integration with PaymentGateway is presented in .Infrastructure and .Application layers. See StartPaymentProcessCommand, FinalizePaymentProcessCommand and PaymentGatewayService. It is described in details in .Infrastructure ReadMe.md file.

To integrate with Payment Gateway we should use the docker compose approach.

Payment Gateway is a dummy application created by me to simulate the payment process. Postman Collection is enhanced with endpoints to test payments.

Open Telemetry πŸ’Ύ

Open telemetry is configured using Open Telemetry Collector that exports signals to:

  1. Jaeger (traces and logs)
  2. Prometheus (metrics)

To display metrics I use Grafana. More information in ReadMe.Persistence.md.

Additionally, I added the Aspire Dashboard to for all signal, for the local development.

Source Generation πŸ„

Source generator is dedicated for Shopway application.

It is configured to generate strongly typed ids, id converters, id comparers, enum converters in the form that Shopway requires.

More information in ReadMe.SourceGenerator.md or ReadMe.Domain.md

Postman Collection 🚧

To get all postman endpoints configured for local environment and docker containers:

  1. Go to docs folder.
  2. Import collection DomainDrivenDesignUniversity.postman_collection.json
  3. Import workspace globals workspace.postman_globals
  4. Import local postman environment LOCAL.postman_environment
  5. Import docker postman environment DOCKER.postman_environment

All collection and global variables are set after requests. For instance, Login endpoint automatically sets a bearer token. Then, this token is used in the authentication process.

GitHub Workflows 🐫

Currently there are two workflows setup for this repository:

  1. Continuous Integration
    • This workflow setups and builds the application. Then, it runs unit tests and publishes test reports.
  2. Conventional Pull Request Validation
    • This workflow validates if the pull request name matches the conventional commit rules. If validation succeeds, it adds a meaningful label to the pull request and assign author to the pull request.