Git Product home page Git Product logo

ddd-example-ecommerce's Introduction

DDD Example Project in Java: eCommerce

The purpose of this project is to provide a sample implementation of an e-commerce product following Domain-Driven Design (DDD) and Service-Oriented Architecture (SOA) principles.

Programming language is Java with heavy use of Spring framework.

# build
./mvnw clean install

# run 
./mvnw spring-boot:run

# open in browser http://localhost:8080

Table of Contents

Domains

Several Business Capabilities have been identified:

Core Domain

  • Sales
    • put a product for sale
    • categorize a product
    • update a product
    • change a product price
    • validate an order
    • place an order

Supporting Subdomains

  • Warehouse

    • stack goods
    • fetch goods for shipping
  • Billing

    • collect a payment
  • Shipping

    • dispatch a delivery

Later, we can think about more supporting domains (not implemented in this project):

  • Marketing

    • discount a product
    • promote a product
  • User Reviews

    • add a product review
  • Customer Care

    • resolve a complain
    • answer a question
    • provide help
    • loyalty program

The e-commerce system is a web application using a Portal component implementing the Backends For Frontends (BFF) pattern.

The idea of Microfrontends is implemented in an alternative branch.

Event Workflow

The communication among domains is implemented via events:

Event Workflow

When the customer places an order the following process starts up (the happy path):

  1. Shipping prepares a new delivery.
  2. Sales creates a new order and publishes the OrderPlaced event.
  3. Shipping accepts the delivery.
  4. Billing collects payment for the order and publishes the PaymentCollected event.
  5. Warehouse fetches goods from the stock and publishes the GoodsFetched event.
  6. Shipping dispatches the delivery and publishes the DeliveryDispatched event.
  7. Warehouse updates the stock.

There is only the basic "happy path" workflow implemented with a big room for improvement, for example when Shipping doesn't get bot Events within a time period, the delivery process should be cancelled etc..

Services Dependencies

Services cooperate together to work out the Business Capabilities: sale and deliver goods.

The actual dependencies come only from Listeners which fulfill the role of the Anti-Corruption Layer and depend only on Domain Events.

Event and Listener

Events contain no Domain Objects.

For communication across Services an Event Publisher abstraction is used, located in the package ..ecommerce.common.events. The interface is an Output Port (in the Hexagonal Architecture) and as a cross-cutting concern is its implementation injected by the Application.

Architectural Overview

While no popular architecture (Onion, Clean, Hexagonal, Trinity) was strictly implemented, the used architectural style follows principles and good practices found over all of them.

  • Low coupling, high cohesion
  • Implementation hiding
  • Rich domain model
  • Separation of concerns
  • The Dependency Rule

The below proposed architecture tries to solve one problem often common for these architectural styles: exposing internals of objects and breaking their encapsulation. The proposed architecture employs full object encapsulation and rejects anti-patterns like Anemic Domain Model or JavaBean. An Object is a solid unit of behavior. A Service is an Object on higher level of architectural abstraction.

Screaming Architecture

The architecture "screams" its intentions just by looking at the code structure:

..ecommerce
    billing
        payment
    sales
        category
        order
        product
    shipping
        delivery
    warehouse

Going deeper the technical concepts are visible too:

..ecommerce
    billing
        payment
            jdbc
        listeners
        rest

Packaging

As shown in the previous section, the code is structured by the domain together with packages for technical concerns (jdbc, rest, web, etc.).

Such a packaging style is the first step for a further modularization.

The semantic of a package is following: company.product.domain.service.[entity|impl], where entity and impl are optional. Full example: com.ttulka.ecommerce.billing.payment.jdbc.

Assembling

While a physically monolithic deployment is okay for most cases, a logically monolithic design, where everything is coupled with everything, is evil.

To show that the Monolith architectural pattern is not equal to the Big Ball Of Mud, a modular monolithic architecture was chosen as the start point.

The services can be further cut into separate modules (eg. Maven artifacts) by feature:

com.ttulka.ecommerce:ecommerce-application
com.ttulka.ecommerce.sales:catalog-service
com.ttulka.ecommerce.sales:cart-service
com.ttulka.ecommerce.sales:order-service
com.ttulka.ecommerce.billing:payment-service
com.ttulka.ecommerce.shipping:delivery-service
com.ttulka.ecommerce.warehouse:warehouse-service

Or by component:

com.ttulka.ecommerce.billing:payment-domain
com.ttulka.ecommerce.billing:payment-jdbc
com.ttulka.ecommerce.billing:payment-rest
com.ttulka.ecommerce.billing:payment-events
com.ttulka.ecommerce.billing:payment-listeners

In detail:

com.ttulka.ecommerce.billing:payment-domain
    ..billing
        payment
            Payment
            PaymentId
            CollectPayment
            FindPayments
com.ttulka.ecommerce.billing:payment-jdbc
    ..billing.payment.jdbc
        PaymentJdbc
        CollectPaymentJdbc   
        FindPaymentsJdbc     
com.ttulka.ecommerce.billing:payment-rest
    ..billing.payment.rest
        PaymentController
com.ttulka.ecommerce.billing:payment-events
    ..billing.payment
        PaymentCollected
com.ttulka.ecommerce.billing:payment-listeners
    ..billing.payment.listeners
        OrderPlacedListener

Which can be brought together with a Spring Boot Starter, containing only Configuration classes and dependencies on other modules:

com.ttulka.ecommerce.billing:payment-spring-boot-starter
    ..billing.payment
        jdbc
            PaymentJdbcConfig
        listeners
            PaymentListenersConfig
    META-INF
        spring.factories

Note: Events are actually part of the domain, that's why they are in the package ..ecommerce.billing.payment and not in ..ecommerce.billing.payment.events. They are in a separate module to break the build cyclic dependencies: a dependent module (Listener) needs to know only Events and not the entire Domain.

See this approach in an alternative branch: modulith.

Anatomy of a Service

Service is the technical authority for a specific business capability.

  • There is a one-to-one mapping between a Bounded Context and a Subdomain (ideal case).
  • A Bounded Context defines the boundaries of the biggest services possible.
  • A Bounded Context can be decomposed into multiple service boundaries.
    • For example, Sales domain contains Catalog, Cart and Order services.
  • A service boundaries are based on service responsibilities and behavior.
  • A service is defined by its logical boundaries, not a physical deployment unit.

Application is a deployment unit. A monolithic Application can have more Services.

  • Bootstrap (application container etc.).
  • Cross-cutting concerns (security, transactions, messaging, logging, etc.).

Application and Services

Configuration assemblies the Service as a single component.

  • Has dependencies to all inner layers.
  • Can be implemented by Spring's context @Configuration or simply by object composition and Dependency Injection.
  • Implements the Dependency Inversion Principle.

Gateways create the published API of the Service.

  • Driving Adapters in the Hexagonal Architecture.
  • REST, SOAP, or web Controllers,
  • Event Listeners,
  • CLI.

Use-Cases are entry points to the service capabilities and together with Entities form the Domain API.

  • Ports in the Hexagonal Architecture.
  • No implementation details.
  • None or minimal dependencies.

Domain Implementation fulfills the Business Capabilities with particular technologies.

  • Driven Adapters in the Hexagonal Architecture.
  • Tools and libraries,
  • persistence,
  • external interfaces access.

Source code dependencies point always inwards and, except Configuration, are strict: allows coupling only to the one layer below it (for example, Gateways mustn't call Entities directly, etc.).

Service Anatomy

Example of a Service Anatomy

As a concrete example consider the Business Capability to find payments in Billing service:

  • Application is implemented via Spring Boot Application.
  • PaymentJdbcConfig configures the JDBC implementations for the Domain.
  • Gateway is implemented as a REST Controller.
  • Use-Case interface FindPayments is implemented with PaymentsJdbc in Use-Cases Implementation.
  • Entity Payment is implemented with PaymentJdbc in Entities Implementation.

Service Anatomy

There is no arrow from Configuration to Gateways because PaymentController is annotated with Spring's @Component which makes it available for component scanning the application is based on. This is only one possible approach. Another option would be to put the Controller as a Bean into the Configuration, etc..

Conclusion

The goal of this project is to demonstrate basic principles of Domain-Driven Design in a simple but non-trivial example.

For the sake of simplicity a very well-known domain (e-commerce) was chosen. As every domain differs in context of business, several assumption must have been made.

Although all fundamental use-case were implemented, there is still a room for improvement. Cross-cutting concerns like authentication, authorization or monitoring are not implemented.

Where to Next

Check out the alternative branches and repos to see additional concepts and technologies in action:

  • Modulith: A separate Maven module per service.
  • Microfrontends: Service Web Components as part of the service codebase.
  • Microservices: Deployments with Docker and Kubernetes.
  • Kotlin: The same project again, this time in Kotlin.

ddd-example-ecommerce's People

Contributors

ttulka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ddd-example-ecommerce's Issues

Why to use separate ProductId classes for different parts of the system?

I see the ProductId is always implemented the same way regardless of being used in different subdomains. So what is the motivation behind code duplication?

I also noticed that the ...Id classes are implemented with the same logic in general. Wouldn't it be better if we had an abstract Id class and the concrete classes inherited from it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.