2. Hi, I'm Victor Rentea
Java Champion – drinking since 2006
Trainer – 3000+ devs / 80+ companies, since 2013
Speaker – Conferences & Meetups
Hibernate
Spring Java8/FP
Java Performance Secure Coding
Reactive
Architecture Clean Code Unit Testing
3. $
Hibernate
Spring Java8/FP
Architecture Clean Code Unit Testing
Masterclass
Company
Training
Video
Courses
YouTube
Channel
💗 Join My
Community
Blog
@victorrentea
VictorRentea.ro
victorrentea@gmail.com
Java Performance Secure Coding
Reactive
8. 176 VictorRentea.ro
a training by
☢ Beware of Foreign Data Structures ☢
➢ Bloated (more fields than you need)
➢ Flat
➢ Different perspective 🐔 (bounded context)
➢ Fixed design (often generated or in client-lib)
➢ Mutable (pain from frameworks otherwise)
➢ No constraints (nulls, validation, invariants)
➢ Diverging in time (v2)
We 💗 small, cohesive data structures
We 💗 Logic next to data (OOP)
We 💗 Immutable objects
We 💗 Deep, Rich Domain Model
We 💗 Models to guard their invariants
We want control over our structures
Because DTOs are: In our core logic:
We 💗 Structures tailored to our problem
10. 178 VictorRentea.ro
a training by
Bounded Context A
(TeamA)
The Curious Case of Nanoservices
DTOs can be shared
within the same Bounded Context (team)
=tiny microservice
eg. 5 devs maintaining
20 microservices
1
2
3
Bounded Context B
(TeamB)
X
But not with others
What's foreign?
11. 179 VictorRentea.ro
a training by
⚠ Returning entities as JSON ⚠
➢ Couples your clients to your internal entity structure
➢ Is risky to marshal
and of course...
12. 180 VictorRentea.ro
a training by
Decouple
DTO
Domain
Model
< >
Price: more classes + more mapping
Exception:
Boundary Systems
= Huge Adapters without own Domain
Software Ecosystem
A
- Manually Crafted DTO
- Generated DTOs:
XML: .xsd/.wsdl ➔ xjc
JSON: .yaml ➔ swagger-gen
Auto-Generated
Mappers
13. 181 VictorRentea.ro
a training by
Auto-Generated Mappers
Converting Entity DTO with
as long as the field names match,
mapping happens automatically
eg MapStruct
Temptation to keep the models in sync
Entities and DTOs must be allowed to diverge
Customer.firstName CustomerDto.firstName
War Stories: When mapping gets complex, deep knowledge of MapStruct is required
➔ Consider switching to manual mapping (simpler) ?
18. 186 VictorRentea.ro
a training by
Layers
SUB-DOMAINS
Controller
Service
Repository
APIs
order Product user
customer
API
infrastructure
too complex ➔
19. 187 VictorRentea.ro
a training by
Layers
Controller
Repository
APIs
Facade / Application Service
Layered Architecture
DOMAIN Service
20. 188 VictorRentea.ro
a training by
Controller
Repository
call
direction
pull orchestration up,
let Repos trivial
Relaxed Layered Architecture
allows skipping layers
Layered Architecture
DOMAIN Service
Facade / Application Service
21. 190 VictorRentea.ro
a training by
by pushing details in lower-level classes
Muscle
Fascicle
Fiber
Myofibril
Myofilaments
Goal = Simplify the top-level view of your flow
Facade = Separation by Layers of Abstraction
24. 193 VictorRentea.ro
a training by
requirements
Logic
Service
Facade
HOW?
Entities
OOP
1) One class/use-case
GetOrderByIdService - ⚠ tiny class
PlaceOrderService - ⚠ god class
-or-
2) N use-cases in a class
class OrderFacade { ⚠ god class
placeOrder()
getOrderById()
}
5-10%..more?💪
Continuously Extract
ApplicationService DomainService
simplify the most complex flows
25. 194 VictorRentea.ro
a training by
Mapper
DTO
Facade
Facade Domain
Service
Domain
Service Domain Services
Ideas inspired by the book Java EE Patterns - Rethinking Best Practices, by Adam Bien
Push Domain Logic into
Start implementing every use-case in a
Facade
For trivial User Cases
a method is enough
eg. getOrderById
When logic gets complex,
or has to be reused
(evolutionary architecture)
26. 195 VictorRentea.ro
a training by
Mapper
DTO
Facade Domain
Service
Domain
Service
take and return only
Domain Objects or primitives
Don't Depend on DTOs
Domain Boundary
DTOs are Evil
VO
Entity
id
Convert them to your Domain Objects ASAP
Domain Services
27. 196 VictorRentea.ro
a training by
Cohesive Domain Services
OrderService
PlaceOrderService
⚠Bloat Risk, if Order is a large Entity
28. @VictorRentea
197
Facade Roles - Summary
• Converts DTOs Entities
• inline, via [Auto-]Mappers, or Dto constructors/methods.
• Validates inputs
• @javax.validation.NotNull, if ...
• Transaction / use-case
• Except in high-TPS systems
• Orchestrates the workflow of a 🧠 use-case
• By delegating to lower-level components
32. 201 VictorRentea.ro
a training by
External
Service
DTO
Adapter
Domain
Service
Domain
Service
<dependency>
domain infrastructure
33. VictorRentea.ro
202
External
Service
DTO
IAdapter Adapter
implements
class UserApiClient
implements IUserAdapter {
public User getById(id){
<external call>
}
}
interface IUserAdapter {
User getById(id);
}
class OrderService {
@Autowired
IUserAdapter adapter;
...
adapter.getById(id);
}
express your need in
a domain interface…
and implement it in a
lower-level module…
When you need
to call outside…
so nothing foreign
enters your domain.
Domain
Service
Domain
Service
<dependency>
domain infrastructure
34. 203 VictorRentea.ro
a training by
calls
Dependency Inversion Principle
<dependency>
higher-level
module
lower-level
module
"Best of OOP"
- Uncle Bob
Abstractions should not depend on details
Low level classes
are not visible
(SOLID Principles)
Dependency Inversion
35. 204 VictorRentea.ro
a training by
calls
<dependency>
higher-level
module
lower-level
module RMI,
HTTP
gRPC..
FTP
Queue
DB
DTO
Dependency Inversion
36. 206 VictorRentea.ro
a training by
Stop Code Dependencies
from complex logic ➔ externals
Dependency Inversion
Package Dependency Checks - via static code analysis
- by compiler
@Test
public void dependencyInversionTest() {
ArchRuleDefinition
.noClasses().that().resideInAPackage("..domain")
.should().dependOnClassesThat().resideInAPackage("..infra")
.check(new ClassFileImporter().importPackages("my.corp.app"));
}
testImplementation com.tngtech.archunit:archunit-junit4:0.15.0 or NDepend (C#)
https://nx.dev/latest/angular/structure/monorepo-tags
https://github.com/MaibornWolff/ts-arch
38. 209 VictorRentea.ro
a training by
infrastructure
EXTERNAL
API
Value Object
Entity
id
Domain
Service
IAdapter
Onion Architecture
Behold, the famous
Database
domain
Repo
Dep Inv
Dep Inv
DTO
Agnostic Domain
application
DTO
Your
Client
Validation
Separate
Persistence
Model
if DB == enemy
Façade
Mapper
Controller
Msg Handler
Adapter
Spring Data
IRepo
39. 210 VictorRentea.ro
a training by
DTO
Value Object
Entity
id
Domain
Service
application Database
domain
DTO
Onion Architecture
Pragmatic
Agnostic Domain
EXTERNAL
API
Your
Client
Façade
Mapper
Controller
Adapter
IAdapter
IRepo
Dep Inv
"Light-CQRS"
Selecting DTOs directly from queries
40. 211 VictorRentea.ro
a training by
Independent of Intrusive Frameworks
Testable Standalone
without a DB, UI, Web Server, etc...
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
Independent of UI
mobile, web, or desktop
Independent of DB
avoid PL/SQL, no vendor lock-in
Independent of External APIs
= external Bounded Contexts
Is an ORM intrusive?
Keep core logic ...
Agnostic Domain
aka Hexagonal
aka Ports-and-Adapters
aka Clean Architecture
Onion Architecture
Learn Hibernate: https://www.youtube.com/watch?v=iw0tOx7Zbjc
42. 213 VictorRentea.ro
a training by
Anemic Domain Model
("classic" approach)
Rich Domain Model
(Domain-Driven Design)
vs
Getters & Setters for all fields
All logic written in Services
Getters but less setters
Logic using fields of class X stays in X
Self-validating model
eg. Address constructor validates its consistency
Aggregates as consistency boundaries
Invariants enforced by Services
eg. Shipment requires a postal code, unless city=Bucharest
44. 215 VictorRentea.ro
a training by
How many fields it has?
Can you make it immutable?
Tell me about that big entity ...
How do you feel about moving logic inside?
45. 216 VictorRentea.ro
a training by
How many fields it has?
Can you make it immutable?
How do you feel about moving logic inside?
Yes!
(use @Embeddable for JPA)
But, How to identify Value Objects?
Extract Value Objects from Entities:
46. 217 VictorRentea.ro
a training by
Conceptual Whole
Money {amount, currency}
FullName {first, last}
Changes Together
LastModified-Time/-ByUser
How to identify Value Objects?
Screens
InvoicingDetails
Moves Together
PriceComputationInput
47. 218 VictorRentea.ro
a training by
Key Points
DTOs are evil
Build a Deep, Rich Domain Model
Protect your Domain with DIP or ArchUnit
Don't let persistence concerns influence your Model design
Continuously Refactor to keep code suple
The entire team should understand the Design Goals
Be Pragmatic. Think Critically! dogmas