DDD showcase project with problem space analysis, system design and various patterns.
This document is ~10% ready, structure will more or less be the same, order might be changed, and some chapters will be dropped (for example - "solving ambiguity" might not be relevant as it was solved in event storming)
- About
- Domain description
- General assumptions
- System design
4.1. Process discovery
4.2. Solving ambiguity
4.3. Interfaces with other systems
4.3.1. Payment system
4.3.2. Protocols and their performance
4.4. Logical design details
4.4.1. Spring
4.4.2. Project structure and architecture
4.4.2.1. Aggregates
4.4.2.2. Events
4.4.2.3. Events in Repositories
4.4.2.4. Database
4.4.2.4.1. ORM
4.5. Physical code design details
4.5.1. Architecture-code gap
4.5.2. Model-code gap
4.6. Deployment
4.7. Fault tolerance - Performance
5.1. Back of the envelope calculations
5.2. Scalability
5.3. Monitoring - Security
6.1. Measures
6.2. Preventing attacks - discussion - Ways to improve, next phases
- Tests
7.1. BDD
7.2. ArchUnit - How to run
9.1. Requirements
9.2. Building the code
9.3. Running
9.4. Collecting metrics - References
This project is an example how one can use DDD to design a simple application.
In the business of renting there are a couple of participants:
- landlord (or property manager, operationally manages properties)
- property owner (who might be interested in seeing the big picture)
- tenant
- various people who take care of the property (cleaners, plumbers, etc)
A landlord rents properties to multiple tenants. Both the landlord and the tenant can report an issue to be fixed. Issues can have different priorities and properties, like pictures before and after or similar.
The landlord needs to keep track of a list of issues to be solved and their status. He needs to keep track of payments. He sends emails or calls if payment is due.
The landlord also needs to keep track of his properties, he needs to be able to store pictures, maps, schematics.
When the landlord rents the property he first creates an agreement with a tenant, documenting the state of the property and generating a contract. The contract can be modified by adding additional items during the inspection, like a broken cabinet or dented wall. Contract, together with pictures is signed and stored in the system. During the stay, the tenant pays the rent according to the contract. The tenant is able to report issues to be fixed by the landlord. After the stay is completed landlord performs an inspection, after which the property is either to be renovated to ready to be rented. The landlord keeps track of the status of each property and offers only available ones. For each apartment, the landlord is able to see a list of things to fix, as well as aggregate them by priority and types of fixes, to dispatch the TODO lists to his employees.
Process discovery was performed using the Event Storming technique (with the help of the Miro's Event Storming template).
The first step of the event storming, collecting domain events is captured here:
After some refinement board looks like this:
The next step was example mapping. I added this step in order to help me with defining business scenarios, tests for them and come up with the prioritized task list.
The result of the mapping is here:
I did not exhaust all scenarios, and in the real world I would not consider this step as completed, however it allows me to continue with the demo project and, interestingly shows possible changes in the domain description. Which leads to yet another domain event session:
The next step was the detailed process modelling:
- actors
- commands
- read models
- external systems
- time
- automatic domain events
The result of modelling is captured here:
After this step I was ready to answer the following questions:
- What aggregates were discovered?
- Which bounded contexts are discovered?
- Which users trigger which commands?
- Which commands affect which aggregates or external systems and trigger which changes?
- Which aggregates or external systems trigger which events during command processing?
- Which events trigger which policies?
- Which events create which read models for which use cases?
- Which policies call up which new commands?
It leads to the last step of the event storming, software modelling, and the result is:
Event storming clears out ambiguity
##Security
Build pipeline can use JFrog XRay, Amazon Inspector or similar products to detect vulnerabilities and legal risks.
This measure builds on modular monolith and uses
${java.home}/conf/security/java.policy
file to control access to resources for specific packages.
Privileged resources are to be accessed through the doPriviledged
block.
Inheritance might pose a risk in certain cases, like calling overridden method in super in such a way which breaks the super's logic. Thus, as a convention, composition and interfaces will be preferred.
JAR files can be signed to prevent possibility of running the modified code. Java policies should be set in place to allow only signed code to run.
During the processing objects may hold sensitive data in memory. In case of failure there is a risk they end up in either a core dump or in a log file, thus following are to be used:
- work on hashed values whenever possible
- free sensitive data for garbage collection as soon as possible
- if possible - overwrite sensitive data with 0's
- do not log sensitive data
Following those rules is not automated and should be checked during the code review.
This attack is prevented by:
- signing JAR files
- using Java policies
- preventing
-noverify
or-Xverify:none
from being used (operationally)
This is to be defeated by using normalize
and toRealPath
methods to sanitize paths used.
Decision pending. Using OWASP to apply XML sanitation. Relying on prepared statements or JPA whenever possible.
Using OWASP to apply XML sanitation.
Tests are written in a BDD manner, expressing stories defined with Example Mapping. It means I utilize both TDD and Domain Language discovered with Event Storming.
The issue with the growing project is to make sure it follows the vision and, especially when it’s written as a monolith. The danger to avoid is a big ball of mud, with a lack of perceivable design. There are at least three methods to keep the architecture clean and aligned to the design: code review, java modules, and automatic architecture tests. Code review can be easily bypassed or overlooked. Java modules, despite being a great help, cover only one aspect of architecture (dependencies between modules).
Therefore, here I will rely on automatic tests, namely on the ArchUnit, which allows checking more aspects and can provide feedback during the compilation, greatly increasing efficiency.
ArchUnit is a tool that allows you to test the architecture in an easy-to-follow manner. For example, keeping layers (hexagon levels) of abstraction separate can be enforced as follows:
@ArchTest
public static final ArchRule model_should_not_depend_on_infrastructure =
noClasses()
.that()
.resideInAPackage("..model..")
.should()
.dependOnClassesThat()
.resideInAPackage("..infrastructure..");
and that frameworks do not affect the domain model
@ArchTest
public static final ArchRule model_should_not_depend_on_spring =
noClasses()
.that()
.resideInAPackage("..io.pillopl.library.lending..model..")
.should()
.dependOnClassesThat()
.resideInAPackage("org.springframework..");
- Java 11
- Gradle
$ ./gradlew build
- Introducing EventStorming by Alberto Brandolini
- Domain Modelling Made Functional by Scott Wlaschin
- Software Architecture for Developers by Simon Brown
- Clean Architecture by Robert C. Martin
- Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans
- A comprehensive Domain-Driven Design example
- Java SE Platform Security Architecture
- Unit Test Your Architecture with ArchUnit
- [Example