You are on page 1of 90

Check out my ‘Clean Code – The Next Episode’

Deep Dive at Devoxx BE 2019 for more context

Evolving
A Clean, Pragmatic Architecture
A Craftsman's Guide
The Art of Clean Code
= The Next Chapter =
victor.rentea@gmail.com ♦ ♦ @victorrentea ♦ VictorRentea.ro
When something is painful
but you can't avoid doing it…

postpone it
When something is painful
but you can't avoid doing it…

delegate it
When something is painful
but you can't avoid doing it…

Do It More Often!
"Bring The Pain Forward!"
Continuous Integration
Pair Programming
Continuous Refactoring
Test-First

e Xtreme
"Bring The Pain Forward!"
Are you Agile ?
Software Craftsmanship
Technical Practices Professionalism

Deliberate Practice Code of Ethics


Victor Rentea
Java Champion C#

Lead Architect
Internal Coach

Software Craftsman Founder


Pair Programming, Refactoring, TDD

Clean Code Evangelist


best talks on:
VictorRentea.ro/#playlist
Independent
Technical Trainer & Coach
6 years 1500 devs 30 companies 240+ days
Spring Hibernate Java 8
+ Live-Training on

Design Patterns Architecture, DDD

Clean Code Unit Testing, TDD


Posting daily on
Java Performance Scala more…
30K 3K 2K
victor.rentea@gmail.com VictorRentea.ro
VictorRentea.ro @victorrentea victor.rentea@gmail.com
The most important principle
in programming?

14 VictorRentea.ro
Single Responsibility Principle
EmployeeManager
-read/persist
-compute pay-roll vs
-generate PDF report
-manage projects

15 VictorRentea.ro
Coupling
vs

16 VictorRentea.ro
Don’t Repeat Yourself

17 VictorRentea.ro
Keep It Short & Simple
Premature encapsulation is the root of all evil
Overengineering
– Adam Bien
Simpler code Developer Happiness

18 VictorRentea.ro
Protect the Developers
Developer
Invisible Magic to reduce effort and risk Comfort

...

Avoid building an intrusive


Custom Framework
( )
19 VictorRentea.ro
Protect the Developers

Developer
Fear Kills Creativity Simplify Unit Testing
Safety
- Strong regression defense

21 VictorRentea.ro
Building Software

Is this really how it feels ?


23
24 VictorRentea.ro
26 VictorRentea.ro
Can you predict the future ?
What features will grow
super-complex in time?

Keep Struggling for a


Simple Design
At all times!
27 VictorRentea.ro
Overengineering Simple Code
Design Patterns Suple
Useless Abstractions Flexibile

28 VictorRentea.ro
This talk is about…

A Simple Architecture
that Evolves
to Match
the Growing Complexity
30 VictorRentea.ro
Agenda
Core Principles

Modeling Data

Organizing Logic

The Onion Architecture

Tests. Fear.

31 VictorRentea.ro
VictorRentea.ro
Entities = your persistent data
You control them!

Entity

They simplify your logic


32 VictorRentea.ro
public class Customer {
[...]
public String getFullName() {
Put small bits of return firstName + " " + lastName;
}

highly reusable public void activate(User user) {


if (status != Status.DRAFT) {
throw new IllegalStateException();
domain logic in your }
status = Status.ACTIVE;
activatedBy = user;
Domain Entities activatedDate = new Date();
}

public boolean isActive() {


return status == Status.ACTIVE;
}

public boolean canPlaceOrders() {


return status == Status.ACTIVE && !isBann
}
33 VictorRentea.ro
activatedBy = user;
activatedDate = new Date();
}
Put small bits of
public boolean isActive() {
return status == Status.ACTIVE;
highly reusable }

public boolean canPlaceOrders() {


domain logic in your return status == Status.ACTIVE && !isBann
}

Fit Domain Entities public void addAddress(Address address) {


address.setCustomer(this);
addresses.add(address);

BlOAt dAnGeR
}

public List<Address> getAddresses() {


public String toExportString() { return unmodifiableList(addresses);
return String.format("%s;%s;%d", }
firstName, lastName, isActive()?1:0); }
}
34 VictorRentea.ro
Value Object: a grouping of domain data
public class Money {
private final Currency currency; Small
private final BigDecimal amount;
Immutable
public Money(Currency currency,
Lombok?
BigDecimal amount) {
this.currency = currency;
this.amount = amount; No persistent ID
validate();
} unlike an Entity
public Currency getCurrency() {
return currency;
} Equal by value
public BigDecimal getAmount() { of all fields
return amount;
} id
public boolean equals(Object other) @Embeddable Entity VO
{ ... }
} decompose large entities
35 VictorRentea.ro
Then, you expose data to UI

“canBeDeleted”: true

UI has different goals


Given you have some
domain logic to implement

Never expose your Entities in your API


36 VictorRentea.ro
Instead, expose in your API public class CustomerDto {
private String fullName;

Data Transfer Objects private String phoneNumber;


private Date birthDate;

public final String getFullName


return fullName;
Form/Request }
public final void setFullName(S
View/Response DTO id
Entity VO
this.fullName = fullName;
}
DTO public final String getPhoneNum
return phoneNumber;
Logic
SearchCriteria/SearchResult }

"Light CQRS" public final void setPhoneNumbe


this.phoneNumber = phoneNumbe
}
public class
public CustomerDto
final {
Date getBirthDate(

I like dumb DTOs public


}
String
return
public
fullName;
birthDate;
String phoneNumber;
public final void setBirthDate(
(you'll see soon why) public Date birthDate;
this.birthDate = birthDate;
} }
public fields ?! ! !.. }
dto.fullName = customer.getFullName();

37 VictorRentea.ro
Agenda
Core Principles

Modeling Data

Organizing Logic

The Onion Architecture

Tests. Fear.

38 VictorRentea.ro
39 VictorRentea.ro
"canBeDeleted":true
DTO ~ JSON
Controller
Validation Mapping
Transactions
Facade
≈ Application Service [DDD]
calls

Service Entity

Repository Infrastructure
? DTO

VictorRentea.ro
41 VictorRentea.ro
Data Conversion
Complex? ➔ Extract Mappers
API Domain

Mapper
DTO
id
Entity VO

➔ DTO constructors
Logic CustomerDto();
CustomerDto dto = new CustomerDto(customer);
dto.fullName = customer.getFullName();
dto.birthDate = customer.getBirthDate();
dto.phoneNumber = customer.getPhoneNumber();
42 VictorRentea.ro
Validation
43 VictorRentea.ro
Validation
44 VictorRentea.ro
Validation
only fields always true

➢internal ➢ubiquitous ➢@NotNull ➢on DTO


➢external ➢status-specific ➢if ➢on Entity
queries null name is OK @Autowired
at saveDraft() ➢both
@ValidCity
validation groups
45 VictorRentea.ro
Start writing all
domain logic in a

Facade
HOW?
Logic
OOP

Service
VO
Entities

https://www.amazon.com/Domain-Driven-Design-
Tackling-Complexity-Software/dp/0321125215
VictorRentea.ro
Evolutionary Start writing all domain logic in a
Approach: Facade
Then extract logic into
Mapper id
Entity VO
Domain Services

Facade Domain - To hide complexity: SRP


DTO Service
- For Reuse: DRY
▪ between Facades or Services
Facade

57 Read more about this approach in Java EE Patterns - Rethinking Best Practices, by Adam Bien VictorRentea.ro
Domain Services
speak your Domain Model
parameters and return types

id
Mapper Entity VO
DTOs are fragile
under enemy control
* I like them dumb (no logic) Facade Domain
DTO Service

Keep DTOs out! Facade


Convert them to your Domain Objects ASAP

58 VictorRentea.ro
Aspects
- Transactions
Convert Data Facade Roles
- Logging Mapper
- Exception Handling*
- Access Control*

id
Entity VO
Controller Façade
DTO Domain
Service

Validator
Validate Data Implement Logic

VictorRentea.ro
What’s that? How? Huh!? Then I extract? Piece a cake!
That’s it?

Extract when it Grows


A Good
Name

He-he!☺
When a class “There are only two things
grows too big Look for a good class
hard in programming:
(>~200 lines?) name that summarizes
Yup! Cache Invalidation and
➔ break it some of its methods
Naming Things”
VictorRentea.ro
SRP
applied to reduce complexity

not to generate 10-lines classes


61 VictorRentea.ro
62 VictorRentea.ro
63 VictorRentea.ro
Extract when it Grows
Horizontal Splitting
Controller

CustomerOpsFacade
CustomerFacade CustomerPreferencesFacade
saveCustomer() saveCustomerPreferences()
getCustomer() getCustomerPreferences()
searchCustomer() validateAddress()
resetPassword()
saveCustomerPreferences() checkPassworStrength()
getCustomerPreferences()
validateAddress()
resetPassword()
checkPassworStrength() at the same level of abstraction
> 300 lines
VictorRentea.ro
Extract when it Grows
Vertical Extraction

OrderService DeliveryService

CompositeOrder more abstract,


EmailService
Service higher-level

Separation by Layers of Abstraction


65 VictorRentea.ro
Practice

Pair Programming
VictorRentea.ro
VictorRentea.ro
69
http://wiki.c2.com/?PairProgramming
Developer Comfort
is essential for
Emerging Architectures

VictorRentea.ro
n

Jr. Architect
(prev: Jr JS Developer)

(last LOC: 10+ years ago)


VictorRentea.ro
Agenda
Core Principles

Modeling Data

Organizing Logic

The Onion Architecture

Tests. Fear.

72 VictorRentea.ro
VictorRentea.ro
The code you want to protect

Domain Objects
id
Entity VO

Domain
Service

Priceless Domain Logic

Put it in the domain module


73 VictorRentea.ro
Mapper

DTO id
Entity VO
application

Domain
Façade
Service

Validator domain

depends on domain
74 VictorRentea.ro
domain

Domain External
Service Service

DTO

75 VictorRentea.ro
domain <dependency> infrastructure

Domain External
Adapter
Service Service
DTO

VictorRentea.ro
<dependency>
domain infrastructure
When you need express your need in and implement it in a so nothing foreign
to call outside… a domain interface… lower-level module… enters your domain.

Domain External
IAdapter Adapter
Service implements Service
DTO

class OrderService { interface IOrderRepo { class OrderRepository


@Autowired Order getById(id); implements IOrderRepo {
IOrderRepository repo; }
... { public Order getById(id){
repo.getById(id); ...
} }
} }
77 VictorRentea.ro
Dependency Inversion Principle
Abstractions should not depend on details

Low level classes


are not visible

<dependency>
higher-level lower-level
module calls module

"Best of OOP"
- Uncle Bob

78 VictorRentea.ro
lower-level
module

DTO HTTP, RMI, …

<dependency>
higher-level JMS
module calls

FTP

DB

80 VictorRentea.ro
An agnostic Domain
lets you focus on
YOUR logic

VictorRentea.ro
What code would you protect?

Domain Objects
Interfaces for
id
Entity VO External Services
IExtSrv you consume
Domain Adapter

Service
Priceless Domain Logic IRepo
Interfaces for
Repositories

Put it in the domain module


84 VictorRentea.ro
Behold,
The Onion Architecture
a.k.a. Hexagonal, Ports-and-Adapters
Mapper

DTO id
Entity VO
IExtSrv
application

infra
Domain Adapter ExtSrv
Façade Adapter
Service
IRepo
F

Repo
Validator implem

85 VictorRentea.ro
Mapper

WS DTO
DTO id
Entity VO Interface
IExtSrv
application

infra
Domain Adapter
Façade Adapter
Service
IRepo
F

Repo
Validator implem

96 VictorRentea.ro
Pragmatic
for decent apps, 2 modules are enough
Mapper

WS DTO
DTO id
Entity VO Interface
IExtSrv
Domain Adapter
Façade Adapter
Service
IRepo
F
domain
Repo
Validator implem

97 application VictorRentea.ro
Naming …

98 VictorRentea.ro
Package Names
com.myorg.myapp .facade
.order. …
.service .product. …
.order. …
They grew too big! .entity .product. …
.infra
Let’s restructure it.
Clean Architecture

99
.repo that speaks in domain words
- Uncle Bob
VictorRentea.ro
com.myorg.myapp. Modularizing the Monolith
.order. … Majestic Monolith*

MICRO…
Facade

SELECT
DEPENDS
FK?
EVENT

CALLS
TX Replication
DTO

Infra

.product. … Use-case Optimal Query 😱


"Light CQRS" consistency
100 * Check out Axel Fontaine talks on that for great tips for transitioning from a monolith VictorRentea.ro
101 VictorRentea.ro
archunit.org

ArchRule rule =
classes().that().resideInAPackage("..service..")
.should().onlyBeAccessed()
.byAnyPackage("..controller..", "..service..");

102 VictorRentea.ro
104 VictorRentea.ro
tardigrade
105 VictorRentea.ro
Evolution
Only Happens
Under Constraints
fitness function
106 VictorRentea.ro
fitness function
source file size
coupling
performance
scalability
security

107 VictorRentea.ro
Further Reading

▪ 7 Virtutes of a Good Object ▪ Good software is written 3 times


http://www.javaworld.com/article/2072651/becoming-a-great-programmer--use-your-
▪ NULL – the worst mistake in IT -
trash-can.html
https://dzone.com/articles/the-worst-mistake-of-computer-science-1
-
▪ Prezi-like effect in PowerPoint 2016: “Morph”
▪ The Clean Architecture:
- http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html ▪ Value Objects vs Entity
- http://enterprisecraftsmanship.com/2016/01/11/entity-vs-value-object-the-ultimate-list-of-
▪ Some ☺ Programming Jargon differences/
- http://blog.codinghorror.com/new-programming-jargon/
▪ Extends is bad
▪ Code quality: WTFs/minute - http://www.yegor256.com/2016/09/13/inheritance-is-procedural.html
- http://commadot.com/wtf-per-minute/
▪ SOLID is WRONG
- https://speakerdeck.com/tastapod/why-every-element-of-solid-is-wrong

122 VictorRentea.ro
Agenda
Core Principles

Modeling Data
Organizing Logic
The Onion Architecture

Tests. Fear.

123 VictorRentea.ro
VictorRentea.ro
Takeaways
Agenda
Core Principles

Modeling Data
Organizing Logic
The Onion Architecture

Tests. Fear.

124 VictorRentea.ro
VictorRentea.ro
Takeaways
KISS : Avoid overengineering
Magic to protect your Developers
Modeling Data
Organizing Logic
The Onion Architecture
Tests. Fear.
125 VictorRentea.ro
VictorRentea.ro
Takeaways
KISS : Avoid overengineering
Magic to protect your Developers
Enemy data in your DTOs: keep them out
Organizing Logic
The Onion Architecture
Tests. Fear.
126 VictorRentea.ro
VictorRentea.ro
Takeaways
KISS : Avoid overengineering
Magic to protect your Developers
Enemy data in your DTOs: keep them out
Extract when it Grows : for SRP or DRY

The Onion Architecture


Tests. Fear.
127 VictorRentea.ro
VictorRentea.ro
Takeaways
KISS : Avoid overengineering
Magic to protect your Developers
Enemy data in your DTOs: keep them out
Extract when it Grows : for SRP or DRY

The Onion , DIP: domain agnostic to externals


(Adapt® them)
Tests. Fear.
128 VictorRentea.ro
VictorRentea.ro
Takeaways
KISS : Avoid overengineering
Magic to protect your Developers
Enemy data in your DTOs: keep them out
Extract when it Grows : for SRP or DRY

The Onion , DIP: domain agnostic to externals


(Adapt® them)
Tests: let them smash your design
129 VictorRentea.ro
VictorRentea.ro
KISS
130 VictorRentea.ro
Keep It simple

131 VictorRentea.ro
What's next for you?

VictorRentea.ro
Put Passion In
All That You Do!!

137 VictorRentea.ro
¡¡ PLEASE !!
Hunt me down for questions!

138 VictorRentea.ro
A SCALABILITY PROBLEM:
OUT OF STICKERS
¡¡ PLEASE !! AND KEY CHEAT-SHEETS

Hunt me down for questions!


YOU CAN DOWNLOAD AND PRINT THEM
YOURSELF FROM VICTORRENTEA.RO

Thank You! Check out my


other 2 sessions
at Devoxx Belgium

Speaking Romanian? join me Trainings, talks, goodies Follow me for quality posts:
victorrentea.ro/community VictorRentea.ro @VictorRentea

You might also like