Skip to content

DDD example with problem space analysis and various patterns.

Notifications You must be signed in to change notification settings

mpomaran/landlord

Repository files navigation

CircleCI codecov

landlord

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)

Table of contents

  1. About
  2. Domain description
  3. General assumptions
  4. 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
  5. Performance
    5.1. Back of the envelope calculations
    5.2. Scalability
    5.3. Monitoring
  6. Security
    6.1. Measures
    6.2. Preventing attacks - discussion
  7. Ways to improve, next phases
  8. Tests
    7.1. BDD
    7.2. ArchUnit
  9. How to run
    9.1. Requirements
    9.2. Building the code
    9.3. Running
    9.4. Collecting metrics
  10. References

About

This project is an example how one can use DDD to design a simple application.

Domain description

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.

General assumptions

System design

Process discovery

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: Domain events

After some refinement board looks like this: Domain events refined

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: Example mapping

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:

Domain events refined again

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: Process modelling

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: Software modelling

Solving ambiguity

Event storming clears out ambiguity

Interfaces with other systems

Payment system

Protocols and their performance

Logical design details

Spring

Project structure and architecture

Aggregates
Events
Events in Repositories
Database
ORM

Physical code design details

Architecture-code gap

Model-code gap

Deployment

Fault tolerance

Performance

Back of the envelope calculations

Scalability

Monitoring

##Security

Measures

Security in third-party modules and legal security

Build pipeline can use JFrog XRay, Amazon Inspector or similar products to detect vulnerabilities and legal risks.

Java policies

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.

Composition and interfaces instead of inheritance

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.

Signing JAR files

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.

Protecting sensitive data

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.

Preventing attacks - discussion

Byte code attack

This attack is prevented by:

  • signing JAR files
  • using Java policies
  • preventing -noverify or -Xverify:none from being used (operationally)

Directory structure traversal

This is to be defeated by using normalize and toRealPath methods to sanitize paths used.

SQL injection

Decision pending. Using OWASP to apply XML sanitation. Relying on prepared statements or JPA whenever possible.

DoS

Intercepting sensitive data

Unauthorized access

Attack network socket

Preventing XML injections

Using OWASP to apply XML sanitation.

Ways to improve, next phases

Tests

BDD

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.

ArchUnit

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..");

How to run

Requirements

  • Java 11
  • Gradle

Building the code

$ ./gradlew build 

Running

Collecting metrics

References

  1. Introducing EventStorming by Alberto Brandolini
  2. Domain Modelling Made Functional by Scott Wlaschin
  3. Software Architecture for Developers by Simon Brown
  4. Clean Architecture by Robert C. Martin
  5. Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans
  6. A comprehensive Domain-Driven Design example
  7. Java SE Platform Security Architecture
  8. Unit Test Your Architecture with ArchUnit
  9. [Example

About

DDD example with problem space analysis and various patterns.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages