Contribute to help us improve!
Are there edge cases or problems that we didn't consider? Is there a technical pitfall that we should add? Did we miss a comma in a sentence?
If you have any input for us, we would love to hear from you and appreciate every contribution. Our goal is to learn from projects for projects such that nobody has to reinvent the wheel.
Let's collect our experiences together to make room to explore the novel!
To contribute click on Contribute to this page on the toolbar.
Layered Architecture
The layered architecture is a technical architecture view, that divides the applications business components into technical layers based on the multilayered architecture.
Introduction and Goals
This article porposes a concrete guideline on implementing a layered architecture. This includes the definition of layers, a rule set on dependencies between those layers and a concrete proposal for folder and package names.
Context and scope
-
Focus on the technical architecture. No proposals on business splits will be made.
-
The layered architecture can be used in services of a microservice architecture or in monoliths. The focus here is on a single deployable artifact independent of its size. The layered architecture is independent of the higher architecture.
-
Only back-end services are considered here.
Solution Strategy
This chapter explains the layered architecture, the single layers and rules to follow.
Reference Architecture
Back-end service
The Layered Architecture Reference focuses on the back-end service A that consists of two business components. The components are divided into following layers:
- Service layer
-
Exposes functionality of the back-end to the client or other consumers.
- Batch layer
-
Exposes functionality to be used in batch-processes (e.g. mass imports).
- Logic layer
-
Contains the business logic.
- Dataaccess layer
-
The dataaccess layer manages the entities including their storage and retrieval.
Front-end service
The client uses the service endpoints of the back-end service. It is divided into the same business components as the backend with only the client layer.
Front-end services are not in focus of the Java stack. The client layer is added for completeness. devonfw supports clients based on single page applications with Angular with the typescript stack. |
Dependencies
The dependencies and call directions follow a few strict rules.
Dependencies inside a component
In the layered architecture reference those dependencies are displayed by a blue connector. Inside the same component (e.g. Component A1) the following rules adhere:
-
Each layer can only depend on resources from the same layer or from the layer below. A1 UseCases in the logic layer might depend on the interfaces from the domain layer or have a dependency to another use-case from the same component in the logic layer.
-
A layer can never depend on a layer above. The dataaccess layer should never depend on the logic layer.
-
Layers should not be skipped. The service layer should not depend on the dataaccess layer directly.
Dependencies between components
In the layered architecture reference those dependencies are displayed by a orange connector. Inside the same service, but across components (e.g. Component A1 calls Component A2)the following rules adhere:
-
The business architecture defines which components can depend on each other. It highly depends on the business rules if two components are allowed to depend on each other For the next rules it is assumed that Component A1 is allowed to call Component A2.
-
Only the logic layer can have dependencies on other use cases from other components, because it is a business dependency. The logic layer of Component A1 depends on the logic layer of Component A2.
-
The Service layer of Component A1 should not have a dependency to any other layer in Component A2
-
The Logic Layer of Component A1 should not have a dependency to the dataaccess or the service layer in Component A2
-
The Dataaccess Layer of Component A1 should not have a dependency to any other layer in Component A2
Dependencies to external service
In the layered architecture reference those dependencies are displayed by a red connector. Usually services do not work alone, but need further functionality that is made available via interfaces. Dependencies between external services are the most critical ones, because such services are usually not under the teams control. Therefore only a few, but very precise and strict rules are defined:
-
The business architecture defines which service depend on each other.
-
External services can only depend on the exposed interfaces of the service layer. Other layer should never expose their functionality to external services.
-
The only layer that can depend on external services is the logic layer.
Constraints and alternatives
For services with a very limited number of use cases and exposed endpoints, that are also planned to stay small, the three layers might be an overhead. In such cases it might be an option to combine the logic and service layer. Logic and dataaccess layers should never be combined.
For large modulith systems further rules and stricter divisions might be necessary. A possible way to handle this is to keep API and Implementation even stricter separated.
Concrete Implementation
Folder structure
The folder structure should be aligned with the maven standard directory layout.
All sources belonging to the project should be located under src/main
in corresponding subfolders.
Obvious sources are Java source code and static resources that are used.
Other sources like Dockerfiles, helm charts, kubernetes configurations, scripts, etc. should be located in src/main
as well.
The following examples shows a potential folder structure, that might be extended based on the need.
Folders should only be created and used if they have content. |
├──/src
| ├──/main
| | ├──/docker
| | ├──/helm
| | ├──/java
| | └──/resources
| └──/test
| ├──/java
| └──/resources
└──/pom.xml
Package structure
The package structure inside src/main/java
should represent the (business) components and align with the layered architecture.
«root».«component».«layer»[.«detail»]
Segment | Description | Example |
---|---|---|
«root» |
The basic Java package namespace of the application.
According to the Java package name rules the root should consist of |
|
«component» |
The (business) component the code belongs to. It is defined by the business architecture and uses terms from the business domain. Use the implicit component |
|
«layer» |
The technical layers the code belongs to. The layers are described below. |
|
«detail» |
Depending on the size of the application it might make sense to further divide the layers. The table below gives hints on possible division criteria, but it might make sense to add further detail packages or to leave them out. |
|
The layers as packages
The layers and their functionality are described in the back-end service reference. The following table explains the layers and their possible detail packages:
«layer» | «detail» |
---|---|
|
In case multiple protocols are used in the service layer, it might make sense to create a «detail» package for each protocol. Typical protocols are REST or gRPC. For versioning of the APIs further sub-packages can be created. |
|
No details expected |
|
Use |
|
If common gets that big that further separation is necessary detail packages can be created. A high amount of layer unrelated implementations is often considered an anti pattern. Check if common functionality might fit into other layers. |
Reference structure
«root»
├──.«component»
| ├──.dataaccess
| | ├──.repository
| | | ├──.«BusinessObject»Repository
| | | └──.«BusinessObject»Fragment
| | ├──.dao [alternative to repo]
| | | └──.«BusinessObject»Dao
| | └──.model
| | └──.«BusinessObject»Entity
| ├──.logic
| | ├──«BusinessObject»Validator
| | └──«BusinessObject»EventsEmitter
| | └──.Uc«Operation»«BusinessObject»
| └──.service
| └──.rest
| └──.v1
| ├──.«Component»RestService
| ├──.mapper
| | └──.«BusinessObject»Mapper
| └──.model
| └──.«BusinessObject»Dto
└──.general
└──.dataaccess
└──.model
└──.ApplicationPersistenceEntity
Example
com.devonfw.application.mts
├──.bookingmanagement
| ├──.dataaccess
| | ├──.repository
| | | ├──.BookingRepository.java
| | | ├──.InvitedGuestRepository.java
| | | └──.TableRepository.java
| | └──.model
| | ├──.BookingEntity.java
| | ├──.InvitedGuestEntity.java
| | └──.TableEntity.java
| ├──.logic
| | └──BookingManagement.java
| └──.service
| ├──.mapper
| | ├──.BookingMapper.java
| | ├──.InvitedGuestMapper.java
| | └──.TableMapper.java
| ├──.model
| | ├──.BookingCto.java
| | ├──.InvitedGuestCto.java
| | └──.TableCto.java
| └──.BookingManagementRestService.java
├──.dishmanagement
| ├──.dataaccess
| | ├──.repository
| | | ├──.CategoryRepository.java
| | | ├──.DishRepository.java
| | | └──.IngredientRepository.java
| | └──.model
| | ├──.CategoryEntity.java
| | ├──.DishEntity.java
| | └──.IngredientEntity.java
| ├──.logic
| | └──DishManagement.java
| └──.service
| └──.rest
| └──.v1
| ├──.mapper
| | ├──.CategoryMapper.java
| | ├──.DishMapper.java
| | └──.IngredientMapper.java
| ├──.model
| | ├──.CategoryTo.java
| | ├──.DishTo.java
| | └──.IngredientTo.java
| └──.DishManagementService.java
├──. ...
└──.general
├──.common
| └──.base
| ├──.JWTAuthenticationFilter.java
| ├──.QrCodeService.java
| └──.ValidationService.java
├──.dataaccess
| ├──.repository
| | └──.BinaryObjectRepository.java
| └──.model
| ├──.BinaryObjectEntity.java
| └──.ApplicationPersistenceEntity.java
└──.service
└──.rest
└──.v1
└──.ApplicationAccessDeniedService.java