A sample project following Domain Driven Design with Spring Data JPA
(C) Christoph Knabe, 2017-03-17 ... 2022-01-14
In this project I am trying to apply principles of Domain Driven Design. In contrary to more complex DDD examples on the web I am applying here some simplifications. This sample application has been developed for a course on Software Engineering at Berlin University of Applied Sciences and Technology (Berliner Hochschule für Technik: BHT).
This project uses
- JDK 11 as the platform
- Maven 3 as build tool
- Spring Boot 2 as enterprise framework and web server
- Spring Data + JPA + Hibernate as persistence API and object-relational mapper
- Apache Derby as relational database
- springfox-swagger for generating documentation and user interface for the REST service
- JUnit 4 as unit testing framework
- Mockito 4 for injecting errors into the persistence layer for unit tests
- MulTEx as Exception Handling and Reporting Framework
Detailed version indications you can find in the file pom.xml.
If the correct JDK and Maven versions are installed you can simply use
mvn clean package site
This will fetch all necessary libraries, compile the project, collect the exception message texts,
execute the test suite, package all into an executable .jar file, and generate the reports and project site.
The most interesting report is about Code Coverage.
If you experience problems due to versioning of JDK and Maven, see the chapter about it in document Maven and IDE Integration.
After this is working you can import the Maven project into your Java IDE (Spring Tools may be preferred, see the chapter about IDE Integration in Maven and IDE Integration)
You can run the application (a REST server) in your IDE by running class de.beuth.knabe.spring_ddd_bank.Application as Java Application or on the command line after
mvn package
by
java -jar target/spring-ddd-bank-0.1-SNAPSHOT.jar
This will start the web server Tomcat with the web application Spring DDD Bank. The latter runs the database Derby in embedded mode for the web application, but also offers network access to the database. In the last lines of the log you will see messages like the following:
Tomcat started on port(s): 8080 (http)
Apache Derby Network Server 10.12.1.1 - (1704137) has been started and is ready to accept connections on port 1527.
In these messages you can see the port of the web server (8080), and of the Derby database (1527).
You can shutdown it by typing <Ctrl/C>. Killing it in IntelliJ IDEA or Spring Tools by the red icon also stopped it without a visible negative effect.
-
Modeling the domain layer as one package, which does not depend on any other package besides standard Java SE packages as
java.time
andjavax.persistence
. The latter only for the JPA annotations. -
Prefer a Rich Domain Model over an Anemic Domain Model by having relevant business logic methods in entity class Client. This required Domain Object Dependency Injection (DODI), but is now done manually by method
Client.provideWith
. (The former automatic DODI on Java 8 by AspectJ compile-time weaving was abandoned, as there were serious configuration issues with AspectJ on Java 11. AspectJ seems to become less popular.) -
The Domain Layer references required services only by self-defined, minimal interfaces (in package domain.imports).
-
Implementing required services in the infrastructure layer (in package infrastructure).
-
Linking together required services and their implementations by Spring Dependency Injection.
-
Implementing an interface layer for external access to the application. This is implemented as a REST service in package rest_interface.
- It is a little bank application, where the bank can create clients and clients can create and manage accounts, e.g. deposit and transfer money.
- The analysis class diagram is in file src/doc/BankModel.pdf. Its editable source by UMLet has extension
.uxf
. - An overview of the REST endpoints is given at src/doc/REST-API.md. Additionally when running the REST service, the REST endpoint documentation generated by Swagger, is accessible at http://localhost:8080/ and clicking on REST API documentation.
- As the application layer in this didactical example would only manage transactions, it is omitted, but the same effect is achieved by making the interface layer transactional by the annotation
@Transactional
to class ApplicationController. - Internationalizable, parameterizable exception message texts
- Capture of each exception message text in the reference language (english) directly as main JavaDoc comment of the exception
- The application runs against a file-based Derby database. This is configured in file src/main/resources/application.properties
- Tests are run against an empty in-memory Derby database. This is configured in file src/test/resources/application.properties
- Generation of a test coverage report by the JaCoCo Maven plugin into target/site/jacoco-ut/index.html.
- Demo-only Spring Security with a fixed number of predefined users (1 bank, and 4 clients).
In the file MessageText.properties
. The editable original with some fixed message texts is in src/main/resources/.
By Maven phase compile
this file is copied to target/classes/
.
During the following phase process-classes
the exception message texts are extracted from the JavaDoc comments of all exceptions under src/main/java/
by the ExceptionMessagesDoclet
as configured for the maven-javadoc-plugin
. They are appended to the message text file in target/classes/
.
This process is excluded from m2e lifecycle mapping in the pom.xml
.
- Make Amount a better Value Object by freezing its attributes. Seems, that for this goal Hibernate has to be used instead of JPA.
- Nice to have: Avoid own ID of AccountAccess, because this class models an m:n association between Client and Account.
There should not be a possibility for several links between a client and an account.
This would require the usage of
client.id
andaccount.id
as a composite ID forAccountAccess
. Not so easy, see JPA: How to associate entities with relationship attributes? - Change how the Repository interfaces are implemented in package infrastructure, as it is confusing to beginners, that you implement something by writing an interface (the Spring Data way). We could for example use the JPA Criteria API, or JPQL. This would more look like a real implementation.
- Detailed example text about DDD
- The DDD Sample project, from which are taken some inspirations
- The Ports and Adapters Pattern
- Can DDD be Adequately Implemented Without DI and AOP?
- Spring Boot
- Spring Dependency Injection
- Spring Data JPA
- Spring Data JPA Query Methods
- This application condenses my knowledge about layering and data access at a given point of time. Previous versions were in a 3-layer form in german under Bank3Tier - Bankanwendung in 3 Schichten.