Home Ecommerce DDD - Hands-on Domain-driven Design and Event Sourcing - 1/6
Post
Cancel

Ecommerce DDD - Hands-on Domain-driven Design and Event Sourcing - 1/6



A quick overview of this project


It has been a while since I decided to start this project. It began as the type of developer’s sandbox for combining exciting things and seeing how they get along. As you’ll find in all of the projects I present in my blog, this one has no pretension to be a postulate of any subject but instead describes the steps I followed to learn something of interest. This humble and personal take may spark the interest of beginners in delving deeper into the topics I scratch here.

Suppose you check the project’s Git history; you’ll notice it changed from a simple monolith, barely touching the concept of event sourcing and a very thin limiter for the bounded contexts delimited by a namespacing scheme, to gradually moving to a more robust microservice-based architecture, yet very straightforward implementation of event sourcing. I called this whole refactoring "version 2".

It’s also worth mentioning that such a move started to bring a lot of accidental complexity, the reason why I had to cut or replan things to keep them simple. However, increasing complexity is inevitable due to the need to handle new needs.

Moving from one architectural approach to another needs a good plan and complete awareness of the complexity of maintaining and scaling your application. I advocate that architecture evolves as needed and not for mere deliberate choice. A decentralized design requires more mechanisms to handle the load, connect boundaries, and revert transactions when needed.

If you are already well versed in Domain-driven Design or Event Sourcing and are more interested in checking how to implement Event Sourcing using Marten, you likely jump straight to Part 4.


Why and when using Domain-driven Design?


Our field is full of buzzwords, usually shrank to mnemonics, short in length but carrying a world of complexity. There are countless times I’ve seen recruiters repeating these terms without knowing what it is about but expecting developers to demonstrate years of experience with those things. With domain-driven design, it wouldn’t be different (like any other Whatever-Driven design); it’s broadly spread that it’s about technology, architecture, or a framework, while it’s, in fact, an approach that requires a shift in the way you view things. At the same time, the implementation is just a way to get there.

A fair answer to why using it depends on the nature of the software you’re building. Not all projects will benefit from DDD, period. You don’t want to use it if the domain is not complex enough (remember, Tackling Complexity in the Heart of Software), with tight budgets and time constraints, or technology-centric outcomes (like frameworks, tools). Even if you feel the project is a suitable case for DDD, the lack of domain knowledge (not having at least a domain expert) and an experienced team implementing DDD principles force DDD down the path will feel overwhelming for nothing.

Finding a team fully ready to embrace it is a matter of luck; there’s always the first time for some, which will require effort and discipline to keep it on track. It also needs upper management support because time can be a pressing factor that can easily deviate an inexperienced team that can’t handle the pressure to deliver fast from a good design, forcing the project to mix concerns and lack of attention to details with speeding up everything in a more data-driven design with no commitment to the overall cohesion.

Is object-oriented programming the only way?

Although DDD is agnostic to technology, there’s this misconception that it can be implemented only using object-oriented programming language (like C# or Java) to express the domain through code, and that’s a misconception. I believed in it before reading Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F# by Scott Wlaschin, a good book evidencing that the heart of it is not the paradigm or language you use.

Programming languages are only tools for building paths, not the way. Keeping an open-minded attitude towards technology is healthy. Every person has a bias about something, and developers love to love their favorite stuff, sometimes forgetting they may not be suitable for different needs.


What does implementing domain-driven design look like?


Looking back to 2003, when Eric Evans published the famous blue book, Domain-Driven Design: Tackling Complexity in the Heart of Software, we notice that more than 20 years have passed, and it is still relevant nowadays, if not more than ever. However, the book is abstract and foundational, with insufficient pragmaticity for concrete implementations.

I recommend you read about Strategic and Tactical design in-depth. I also detailed some of these building blocks in this fun project a while ago. There’s also some very literature focused on the implementation with more concrete examples for corporate projects, such as Implementing Domain-Driven Design and Domain-Driven Design Distilled.


Project’s Architecture


The project’s architecture is straightforward. I kept the number of projects as small as possible, so each microservice repeats a vertically sliced structure with everything it needs to keep API, Application, Domain, and Infrastructure well placed. At the same time, the Presentation is held by the SPA alone.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
├── Core
├── Core.Infrastructure
│
├── Crosscutting
│   ├── ApiGateway
│   └── IdentityServer
│
├── Services
│   ├── CustomerManagement
│   ├── InventoryManagement
│   ├── OrderProcessing
│   ├── PaymentProcessing
│   ├── ProductCatalog
|   |   └─ EcommerceDDD.ProductCatalog
│   │      ├── API
│   │      ├── Application
│   │      ├── Domain
│   │      └── Infrastructure
│   ├── QuoteManagement
│   └── ShipmentProcessing
│
├── SPA
└── docker-compose
  • Core
    It defines the building blocks and abstractions used on all underlying projects. Its nature is very abstract, with no implementations.

  • Core.Infrastructure
    It holds some abstractions and implementations for infrastructure to be used by all microservices, such as HTTP communication, message streaming using Kafka, Event Sourcing using MartenDB and lots of third-party NuGet packages, making it conveniently easy to upgrade package versions to all underlying references.

  • Crosscutting
    It contains project implementations that cross-cut all microservices, such as IdentityServer for authentication and authorization, and an API gateway using Ocelot.

  • Services
    The microservices composing the backend are built to be as simple as possible, structured as a vertically sliced structure with API, Application, Domain and Infrastructure.

  • SPA (Single Page Application)
    It’s a SPA built with Angular and TypeScript.

  • docker-compose
    The docker-compose.dcproj is a project containing the docker-compose.yml file, with all the definitions for containers, ports, paths for Dockerfiles and, what we need to run everything within docker containers as I intended.

Inside the microservices


Let’s take the ProductCatalog microservice as an example; this is how the folder structure looks like:

1
2
3
4
5
├── EcommerceDDD.ProductCatalog
│   ├── API
│   ├── Application
│   ├── Domain
│   └── Infrastructure
  • API
    RESTful API for enabling communication between client and server.

  • Application
    It orchestrates the interactions between the external world and the domain to perform application tasks through use cases by handling commands and queries.

  • Domain
    A structured implementation of the domain through aggregates, commands, value objects, domain services, repository definitions, and domain events.

  • Infrastructure
    It is a supporting library for upper layers, handling infrastructural matters such as data persistence with implementing repositories, database mapping, and external integrations.

  • Presentation
    A lightweight Angular-based SPA providing a functional and user-friendly UI.


Final thoughts


I hope you have liked this introduction so far. This series will showcase my thinking process to build a usable application that mimics many challenges we have to deal with in a real-world application for structuring cohesive architecture and transactional flows.

In the next article, I will scratch a bit on Strategic and Tactical designs, the key concepts of DDD. See you there!


Check the project on GitHub



This post is licensed under CC BY 4.0 by the author.