This document outlines an agenda and presentation on clean code architecture. It discusses modeling data with entities and value objects, organizing logic by extracting services and applying the onion architecture. It emphasizes testing to reduce fear, and protecting developers through simplicity and avoiding overengineering. The presentation emphasizes keeping data transfer objects lightweight and external to the domain.
Evolving a Clean, Pragmatic Architecture - A Craftsman's Guide
1. The Art of Clean Code
= The Next Chapter =
victor.rentea@gmail.com ♦ ♦ @victorrentea ♦ VictorRentea.ro
Evolving
A Clean, Pragmatic Architecture
A Craftsman's Guide
Check out my ‘Clean Code – The Next Episode’
Deep Dive at Devoxx BE 2019 for more context
17. VictorRentea.ro18
Keep It Short & Simple
Premature encapsulation is the root of all evilOverengineering
– Adam Bien
Simpler code Developer Happiness
18. VictorRentea.ro19
Invisible Magic to reduce effort and risk
Protect the Developers
...
Developer
Comfort
Avoid building an intrusive
Custom Framework
( )
28. VictorRentea.ro33
Put small bits of
highly reusable
domain logic in your
Domain Entities
public class Customer {
[...]
public String getFullName() {
return firstName + " " + lastName;
}
public void activate(User user) {
if (status != Status.DRAFT) {
throw new IllegalStateException();
}
status = Status.ACTIVE;
activatedBy = user;
activatedDate = new Date();
}
public boolean isActive() {
return status == Status.ACTIVE;
}
public boolean canPlaceOrders() {
return status == Status.ACTIVE && !isBann
}
29. VictorRentea.ro34
activatedBy = user;
activatedDate = new Date();
}
public boolean isActive() {
return status == Status.ACTIVE;
}
public boolean canPlaceOrders() {
return status == Status.ACTIVE && !isBann
}
public void addAddress(Address address) {
address.setCustomer(this);
addresses.add(address);
}
public List<Address> getAddresses() {
return unmodifiableList(addresses);
}
}
public String toExportString() {
return String.format("%s;%s;%d",
firstName, lastName, isActive()?1:0);
}
BlOAt dAnGeR
Fit
Put small bits of
highly reusable
domain logic in your
Domain Entities
30. VictorRentea.ro
Entityid
35
Small
Immutable
Lombok?
No persistent ID
unlike an Entity
Equal by value
of all fields
@Embeddable
decompose large entities
Value Object: a grouping of domain data
VO
public class Money {
private final Currency currency;
private final BigDecimal amount;
public Money(Currency currency,
BigDecimal amount) {
this.currency = currency;
this.amount = amount;
}
public Currency getCurrency() {
return currency;
}
public BigDecimal getAmount() {
return amount;
}
public boolean equals(Object other)
{ ... }
}
validate();
31. VictorRentea.ro
Then, you expose data to UI
36
“canBeDeleted”: true
UI has different goals
Never expose your Entities in your API
Given you have some
domain logic to implement
32. VictorRentea.ro
I like dumb DTOs
(you'll see soon why)
public fields ?! ! !..
VOEntityid
Logic
public class CustomerDto {
private String fullName;
private String phoneNumber;
private Date birthDate;
public final String getFullName
return fullName;
}
public final void setFullName(S
this.fullName = fullName;
}
public final String getPhoneNum
return phoneNumber;
}
public final void setPhoneNumbe
this.phoneNumber = phoneNumbe
}
public final Date getBirthDate(
return birthDate;
}
public final void setBirthDate(
this.birthDate = birthDate;
}
}
37
Form/Request
View/Response
DTO
SearchCriteria/SearchResult
Data Transfer Objects
DTO
Instead, expose in your API
public class CustomerDto {
public String fullName;
public String phoneNumber;
public Date birthDate;
}
dto.fullName = customer.getFullName();
"Light CQRS"
43. VictorRentea.ro58
Keep DTOs out!
parameters and return types
Mapper VOEntityid
DTO
Facade
Facade Domain
Service
Domain
Service
Domain Services
speak your Domain Model
Convert them to your Domain Objects ASAP
DTOs are fragile
under enemy control
* I like them dumb (no logic)
45. VictorRentea.ro
What’s that?
When a class
grows too big
(>~200 lines?)
➔ break it
Extract when it Grows
How?
Look for a good class
name that summarizes
some of its methods
Huh!? Then I extract?
That’s it?
Yup!
Piece a cake!
A Good
Name
He-he!☺
“There are only two things
hard in programming:
Cache Invalidation and
Naming Things”
60. VictorRentea.ro77
External
Service
DTO
IAdapter Adapterimplements
class OrderRepository
implements IOrderRepo {
public Order getById(id){
...
}
}
interface IOrderRepo {
Order getById(id);
}
class OrderService {
@Autowired
IOrderRepository repo;
... {
repo.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
70. VictorRentea.ro
Use-case Optimal Query
100
CALLS
DEPENDS
Modularizing the Monolith
DTO
Facade
Infra
.order. …
.product. …
EVENT
TX
FK?
SELECT
MICRO…
"Light CQRS"
com.myorg.myapp.
consistency
😱
Majestic Monolith*
Replication
* Check out Axel Fontaine talks on that for great tips for transitioning from a monolith
77. VictorRentea.ro
▪ 7 Virtutes of a Good Object
▪ NULL – the worst mistake in IT
- https://dzone.com/articles/the-worst-mistake-of-computer-science-1
▪ The Clean Architecture:
- http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
▪ Some ☺ Programming Jargon
- http://blog.codinghorror.com/new-programming-jargon/
▪ Code quality: WTFs/minute
- http://commadot.com/wtf-per-minute/
▪ SOLID is WRONG
- https://speakerdeck.com/tastapod/why-every-element-of-solid-is-wrong
▪ Good software is written 3 times
- http://www.javaworld.com/article/2072651/becoming-a-great-programmer--use-your-
trash-can.html
▪ Prezi-like effect in PowerPoint 2016: “Morph”
▪ Value Objects vs Entity
- http://enterprisecraftsmanship.com/2016/01/11/entity-vs-value-object-the-ultimate-list-of-
differences/
▪ Extends is bad
- http://www.yegor256.com/2016/09/13/inheritance-is-procedural.html
Further Reading
122
83. VictorRentea.ro128 VictorRentea.ro
Extract when it Grows
The Onion
KISS: Avoid overengineering
Magic to protect your Developers
: for SRP or DRY
Enemy data in your DTOs: keep them out
, DIP: domain agnostic to externals
Tests. Fear.
Takeaways
(Adapt® them)
84. VictorRentea.ro129 VictorRentea.ro
Extract when it Grows
The Onion
KISS: Avoid overengineering
Magic to protect your Developers
: for SRP or DRY
Enemy data in your DTOs: keep them out
, DIP: domain agnostic to externals
Tests: let them smash your design
Takeaways
(Adapt® them)
90. Thank You!
VictorRentea.ro
Trainings, talks, goodies
@VictorRentea
Follow me for quality posts:
victorrentea.ro/community
Speaking Romanian? join me
¡¡ PLEASE !!
Hunt me down for questions!
Check out my
other 2 sessions
at Devoxx Belgium
A SCALABILITY PROBLEM:
OUT OF STICKERS
AND KEY CHEAT-SHEETS
YOU CAN DOWNLOAD AND PRINT THEM
YOURSELF FROM VICTORRENTEA.RO