KEMBAR78
Clean Pragmatic Architecture - Avoiding a Monolith | PPTX
Enterprise Java Training
VictorRentea.ro
victor.rentea@gmail.com
@victorrentea
© Copyright Victor Rentea 2017
Victor Rentea
22 feb 2017
Brainstorming a
Clean Pragmatic Architecture
Single Responsibility Principle
High Cohesion
A method should do only ONE thing.
A class should have only 1 reason to change..?
Enterprise Java Training
VictorRentea.ro
victor.rentea@gmail.com
@victorrentea
© Copyright Victor Rentea 2017
Victor Rentea
22 feb 2017
Brainstorming a
Clean Pragmatic Architecture
Don’t Repeat Yourself
Never duplicate logic (Capital Sin).
Extract-and-Invoke.
Antonym: Write Everything Twice.
Enterprise Java Training
VictorRentea.ro
victor.rentea@gmail.com
@victorrentea
© Copyright Victor Rentea 2017
Victor Rentea
22 feb 2017
Brainstorming a
Clean Pragmatic Architecture
Keep It Short & Simple
Keep It Simple, Stupid - US Navy
Systems work best if kept simple.
Avoid overengineering at all costs.
Enterprise Java Training
VictorRentea.ro
victor.rentea@gmail.com
@victorrentea
© Copyright Victor Rentea 2017
Victor Rentea
22 feb 2017
Brainstorming a
Clean Pragmatic Architecture
Loose Coupling
Classes with few dependencies.
Enterprise Java Training
VictorRentea.ro
victor.rentea@gmail.com
@victorrentea
© Copyright Victor Rentea 2017
Victor Rentea
22 feb 2017
Brainstorming a
Clean Pragmatic Architecture
You Ain’t Gonna Need It
Extreme Programming
Don’t add functionality until deemed necessary.
Implement things when you actually need them,
NOT when you just foresee that you need them.
Enterprise Java Training
VictorRentea.ro
victor.rentea@gmail.com
@victorrentea
© Copyright Victor Rentea 2017
Victor Rentea
22 feb 2017
Brainstorming a
Clean Pragmatic Architecture
Yoda Conditions
if ("a".equals(param)) {
Avoids NPE. Equals FEAR?
Enterprise Java Training
VictorRentea.ro
victor.rentea@gmail.com
@victorrentea
© Copyright Victor Rentea 2017
Victor Rentea
22 feb 2017
Brainstorming a
Clean Pragmatic Architecture
Favor Composition Over Inheritance
GoF
A extends B is bad.
Use composition: a.setB(b);
Enterprise Java Training
VictorRentea.ro
victor.rentea@gmail.com
@victorrentea
© Copyright Victor Rentea 2017
Victor Rentea
22 feb 2017
Brainstorming a
Clean Pragmatic Architecture
NOPping
Stanislav sat watching the screensaver
and nopped for a while.
Enterprise Java Training
VictorRentea.ro
victor.rentea@gmail.com
@victorrentea
© Copyright Victor Rentea 2017
Victor Rentea
22 feb 2017
Brainstorming a
Clean Pragmatic Architecture
Pair Programming
Two programmers working together on one PC.
The driver writes code, the navigator reviews it.
They switch often.
Is cheaper on long-run.
Enterprise Java Training
VictorRentea.ro
victor.rentea@gmail.com
@victorrentea
© Copyright Victor Rentea 2017
Victor Rentea
22 feb 2017
Brainstorming a
Clean Pragmatic Architecture
Dependency Inversion Principle
Abstractions should not depend on details.
Details should depend on abstractions.
Enterprise Java Training
VictorRentea.ro
victor.rentea@gmail.com
@victorrentea
© Copyright Victor Rentea 2017
Victor Rentea
22 feb 2017
Brainstorming a
Clean Pragmatic Architecture
@
VictorRentea.ro
 Spring and
Clean Code, Design Patterns
TDD, Coding Dojos
Java Performance, etc
Victor Rentea
Consultant, Technical Lead
Lead Architect for major IBM client
12
Night Job :
Trainer & CoachClean Code Evangelist
Speaker
victorrentea@gmail.com VictorRentea.ro@victorrentea
VictorRentea.ro13
Driving Principles – KISS
Modeling Data – Enemy data
Organizing Logic – Extract when it Grows
Clean Architecture – The Onion
Tests. Fear.
Agenda
VictorRentea.ro
@
VictorRentea.ro
Single Responsibility Principle
14
EmployeeManager
-read/persist
-compute pay-roll
-generate PDF report
-manage projects
vs
@
VictorRentea.ro
Coupling
15
vs
Read more: https://dzone.com/articles/probably-the-best-package-structure-in-the-world
@
VictorRentea.ro
Don’t Repeat Yourself
16
@
VictorRentea.ro
Keep It Short & Simple
17
Premature encapsulation is the root of all evilOverengineering
– Adam Bien
@
VictorRentea.ro
Premature encapsulation is the root of all evilOverengineering
Keep It Short & Simple
18
@
VictorRentea.ro
Keep It Short & Simple
19
Premature encapsulation is the root of all evilOverengineering
Less, simple code Developer Happiness 
@
VictorRentea.ro
Invisible Magic to reduce effort and risk
Protect the Developers
20
...
Avoid (yet another)
Custom Framework
(to learn)

Developer
Comfort
VictorRentea.ro21
Request/Thread Scope
@Autowired
private MyRequestContext requestContext;
... {
entity.setModifiedBy(requestContext.getCurrentUser());
}
@Component
@Scope(value = "request", proxyMode = TARGET_CLASS)
class MyRequestContext { ... }
@
VictorRentea.ro
Fear Kills Creativity Simplify Unit Testing
- Strong regression defense
Protect the Developers
22
Developer
Safety
@
VictorRentea.ro
Always Think
Regular Brainstorming
23
Regular Refactoring
@
VictorRentea.ro24
Today, 7 April 2017,
I stopped refactoring.
Today, my application became Legacy
@
VictorRentea.ro
Transaction Script
Procedures working with Anemic Entities
- Map easily to real-world business procedures
Domain Driven Design
Rich Entities (OOP) + many XyzServices
- Requires deep business involvement
- Promises easier maintenance on the long-run
- Harder to learn
25
Data
Logic
Where to implement the domain logic ?
or
So I separate logic from data
VictorRentea.ro26
Driving Principles – KISS
Modeling Data – Enemy data
Organizing Logic – Extract when it Grows
Clean Architecture – The Onion
Tests. Fear.
Agenda
VictorRentea.ro
@
VictorRentea.ro
You control them!
Entities hold your persistent data
27
Entity
Logic
Use them to simplify the
implementation of your domain logic
@
VictorRentea.ro
Put small bits of highly
reusable
domain logic in your
Domain Entities
28
public class Customer {
// fields, getters and setters, ...
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
}
@
VictorRentea.ro
Put small bits of highly
reusable
domain logic in your
Domain Entities
29
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 Collections.unmodifiableList(
addresses);
}
}
public String toExportString() {
return String.format("%s;%s;%d",
firstName, lastName, isActive()?1:0);
}
BlOAt dAnGeR
Fit
@
VictorRentea.ro
Entityid
Small: Developer Comfort
Immutable: Developer Safety
Transient: no persistent ID/PK (vs Entity)
Value Object: grouping of domain data that move together
30
Logic
VOpublic class Money {
private Currency currency;
private float amount;
// ...
}
public class Money {
private Currency currency;
private BigDecimal amount;
// ...
}
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();
@
VictorRentea.ro
Then, you start building a User Interface (REST, JSF, ...)
But you quickly realize UI is your enemy.
They want data structures to match the screens.
Their goal is different.
Never expose your Entities to them.
31
Order.isDeletable:boolean
(to show/hide
the Delete button)
@
VictorRentea.ro
I personally like them to be dumb
- public fields ?! !!.. Why not? It’s a struct!
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;
}
}
Form/Request
View/Response
DTO
SearchCriteria/SearchResult
Data Transfer Objects
32
DTO
Instead, give your clients
public class CustomerDto {
public String fullName;
public String phoneNumber;
public Date birthDate;
}
Bare data structures
dto.fullName = customer.getFullName();
Why? You’ll see soon…
VictorRentea.ro33
Driving Principles – KISS
Modeling Data – Enemy data
Organizing Logic
Clean Architecture – The Onion
Tests. Fear.
Agenda
VictorRentea.ro
@
VictorRentea.ro
When DTO  Entity conversion is complex or too long
Extract Mappers to clean up the domain logic
34
VOEntityid
DTO
Mapper
Logic
API Domain
CustomerDto dto = new CustomerDto();
dto.fullName = customer.getFullName();
dto.birthDate = customer.getBirthDate();
dto.phoneNumber = customer.getPhoneNumber();
CustomerDto dto = new CustomerDto(customer);
@
VictorRentea.ro
Mappers go to DB ?
KISS: Yes !
35
@
VictorRentea.ro
Mappers go to DB ?
KISS: Yes !
36
public Customer toEntityForCreate(CustomerDto dto) {
Customer customer = new Customer();
customer.setBirthDate(dto.birthDate);
customer.setGroup(groupRepo.getReference(dto.groupId));...
return customer;
}
public CustomerDto toDto(Customer customer) {
CustomerDto dto = new CustomerDto();
dto.birthDate = customer.getBirthDate();
for (Address a:customer.getAddresses()) {
for (Address a:addressRepo.getByCustomer(customer.getId())){
...
}
return dto;
}
}
Link to
DB entities
Actually,
no DB call
Load more
data from DB
public CustomerDto toDto(Customer customer) {
CustomerDto dto = new CustomerDto();
dto.birthDate = customer.getBirthDate();
for (Address a:customer.getAddresses()) {
for (Address a:addressRepo.getByCustomer(customer.getId())){
...
}
return dto;
} … WHERE ADDR.CUSTOMER_ID = ?
Lazy-load with ORM
Explicit load w/o ORM
N+1 Queries
@
VictorRentea.ro37
N+1 Queries
x 1000
@
VictorRentea.ro
Mappers go to DB ?
KISS: Yes !
38
public Customer toEntityForCreate(CustomerDto dto) {
Customer customer = new Customer();
customer.setBirthDate(dto.birthDate);
customer.setGroup(groupRepo.getReference(dto.groupId));...
return customer;
}
public CustomerDto toDto(Customer customer) {
CustomerDto dto = new CustomerDto();
dto.birthDate = customer.getBirthDate();
for (Address a:customer.getAddresses()) {
for (Address a:addressRepo.getByCustomer(customer.getId())){
...
}
return dto;
}
}
Link to
DB entities
… WHERE ADDR.CUSTOMER_ID = ?
Actually,
no DB call
Load more
data from DB
Lazy-load with ORM
public CustomerDto toDto(Customer customer) {
CustomerDto dto = new CustomerDto();
dto.birthDate = customer.getBirthDate();
for (Address a:customer.getAddresses()) {
for (Address a:addressRepo.getByCustomer(customer.getId())){
...
}
return dto;
}
Explicit load w/o ORM
VictorRentea.ro39
public List<CustomerDto> search(CustomerCriteria criteria) {
List<Customer> customers = customerRepo.search(criteria);
return customers.stream()
.map(customerMapper::toDto)
.collect(toList());
}
CLEAN CODE 
public CustomerDto toDto(Customer customer) {
CustomerDto dto = new CustomerDto();
dto.birthDate = customer.getBirthDate();
for (Address a:customer.getAddresses()) {
for (Address a:addressRepo.getByCustomer(customer.getId())){
...
}
return dto;
}
N+1 Queries
No Prophecies.
Simplicity NOW.
JPQL: LEFT JOIN FETCH customer.addresses
SQL: WHERE ADDR.CUSTOMER_ID IN (?,?,…)
(you know the solution)
VictorRentea.ro40
Measure, Don’t Guess®
Premature encapsulation is the root of all evil
– Adam Bien
optimization
– Donald Knuth
Performance ?
@
VictorRentea.ro
Mapper
Abused DTOs are reused in different use cases
- But some fields are not used in some other cases... Which ones ?!..
41
public class CustomerDto {
public String fullName;
public Date birthDate;
public Long groupId;
public UserTime creation;
public UserTime modification;
}
public class CustomerView extends CustCommDto{
public UserTime creation;
public UserTime modification;
}
public class CustomerCommonDto {
public String fullName;
public Date birthDate;
public Long groupId;
}
Strict DTOs don’t have useless fields
- How to avoid duplication ?
DRY
public CustomerCommonDto common;
- Favor Composition over InheritanceGoF (extends is bad)
@
VictorRentea.ro
You are cheating
when’re DRYing code based on a fragile coincidence
42
(on the client side, the data might be interpreted independently)
@
VictorRentea.ro
You are cheating
when’re DRYing code based on a fragile coincidence
43
Extract constants to explain the logic
Don’t make
a constant here
@
VictorRentea.ro
Start implementing domain logic in a Facade
Extract logic into Domain Services
- To hide complexity – SRP / KISS
- For Reuse (across Facades or Services) - DRY
44
Mapper VOEntityid
DTO
Fç
Façade Domain
Service
Domain
Service
Domain Services
@
VictorRentea.ro
Domain Model
45
Mapper VOEntityid
Fç
Façade Domain
Service
Domain
Service
speak your Domain Model
Keep DTOs out of your logic!
Convert them to your Domain
DTO
Fragile, under enemy control
* That’s why I like dumb DTOs
Domain Services
VictorRentea.ro
Fç
Validate Data
Façade
Convert Data
Implement Logic
Aspects
- Transaction
- Logging
- Global Exception Handling*
46
Facade Roles
DTO
Validator
Mapper
Domain
Service
VOEntityid
VictorRentea.ro47
What do you mean ?
When a class
grows too big
(>~200 lines?)
 break it
Extract when it Grows
How?
Look for a good class
name to group some
of its methods
Huh? If I find a good name,
I extract? That’s it?
Exactly!
Piece a cake!
A Good
Name
He-he!
“There are only two things
hard in programming:
Cache Invalidation and
Naming Things”
@
VictorRentea.ro48
CustomerFacade
saveCustomer()
getCustomer()
searchCustomer()
saveCustomerPreferences()
getCustomerPreferences()
validateAddress()
resetPassword()
checkPassworStrength()
CustomerPreferecesFacade
saveCustomerPreferences()
getCustomerPreferences()
validateAddress()
resetPassword()
checkPassworStrength()
CustomerFacade
saveCustomer()
getCustomer()
searchCustomer()
Extract when it Grows
Same level of abstraction
?!!
@
VictorRentea.ro
In L/XL apps: hard to feel opportunities
 Pair programming, design brainstorming
49
OrderService
AlertService
DeliveryService
Extract for Reuse
Separation by Layers of Abstraction
Increase the Bus Factor
@
VictorRentea.ro
Continuous Practice
Pair Programming
@
VictorRentea.ro
Developer Comfort
is essential for
Emerging Architectures
@
VictorRentea.ro52
VictorRentea.ro53
Driving Principles – KISS
Modeling Data – Enemy data
Organizing Logic – Extract when it Grows
Clean Architecture – The Onion
Tests. Fear.
Agenda
VictorRentea.ro
VictorRentea.ro54
VOEntityid
Domain
Service
Domain
Service
What code would you protect?
Put it in the domain module
Priceless
Domain Logic
Domain Objects
VictorRentea.ro
Façade
DTO
Validator
Mapper
F
application
55
VOEntityid
Domain
Service
Domain
Service
domain
All the other code
depends on domain
VictorRentea.ro56
External
Service
DTO
JAXB
JSON
Domain
Service
Domain
Service
domain
VictorRentea.ro57
External
Service
DTO
JAXB
JSON
Domain
Service
Domain
Service
domain
VictorRentea.ro
ExtServ
Adapter
58
External
Service
DTO
JAXB
JSON
Domain
Service
Domain
Service
domain
VictorRentea.ro59
public class LDAPUserServiceAdapter {
private LdapContext ctx;
...
public LdapUserSearchResult findAccountByAccountName(String ldapSearchBase, String accountName) {
try {
String searchFilter = "(&(objectClass=user)(sAMAccountName={1}))";
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<SearchResult> results = ctx.search(
ldapSearchBase, searchFilter, new Object[]{accountName} searchControls);
if (!results.hasMoreElements())
return null;
}
SearchResult searchResult = (SearchResult) results.nextElement();
if (results.hasMoreElements()) {
throw new MyAppException(ErrorCode.DUPLICATE_USER_IN_LDAP);
}
return new LdapUserSearchResult(searchResult);
} catch (NamingException e) {
throw new MyAppException(ErrorCode.LDAP_LOOKUP_FAILED);
}
}
}
External Service Adapter
.
.
.
.
.
VictorRentea.ro
infra
ExtServ
Adapter
60
External
Service
DTO
JAXB
JSON
Protect your
Domain Model!
Domain
Service
<dependency>
DTOs might slip
in your domain !
domain
VictorRentea.ro
infra
ExtServ
Adapter
61
External
Service
DTO
JAXB
JSON
Protect your
Domain Model!
Domain
Service
IExtServ
Adapter
<dependency>
implements
DTOs can’t get indomain
VictorRentea.ro
low-level modulehigh-level module
ExtServ
Adapter
62
IExtServ
Adapter
<dependency>
implements
at runtime, calls
Higher level modules should not depend on lower-level modules
class OrderRepository
implements IOrderRepo {
public Order getById() {
...
}
}
interface IOrderRepo {
Order getById();
}
Dependency Inversion Principle
Domain
Serviceclass OrderService {
IOrderRepository repo;
...{
repo.getById(id);
}
}
Speaks your
Domain Model
Spring/CDI/Guice/… will inject it
@Autowired
Implement it outside
VictorRentea.ro
infra
ExtServ
Adapter
63
External
Service
DTO
JAXB
JSON
Protect your
Domain Model!
Domain
Service
IExtServ
Adapter
<dependency>
implements
domain
VictorRentea.ro
infra
ExtServ
Adapter
64
External
Service
DTO
JAXB
JSON
IExtServ
Adapter
<dependency>
implements
domain
VictorRentea.ro
infra
ExtServ
Adapter
65
External
Service
DTO
JAXB
JSON
IExtServ
Adapter
<dependency>
implements
HTTP, RMI, …
FTP
JMS
domain
DB
VictorRentea.ro66
VOEntityid
Domain
Service
Domain
Service
IExtSrv
Adapter
IRepo
What code would you protect?
Put it in the domain module
Interfaces for
External Services
you consume
Interfaces for
Repositories
(DB access)
Priceless Domain Logic
Domain Objects
VictorRentea.ro
infra
ExtSrv
Adapter
Repo
implem
67
Façade
DTO
Validator
Mapper
VOEntityid
Domain
Service
Domain
Service
IExtSrv
Adapter
IRepo
F
application
The Onion Architecture
Behold,
a.k.a. Clean, Hexagonal, Ports-and-Adapters
VictorRentea.ro68
infra
Façade
DTO
Validator
Mapper
VOEntityid
Domain
Service
Domain
Service
IExtSrv
Adapter
IRepo
Repo
implem
F
ExtSrv
Adapter
application
The Onion Architecture
Behold,
a.k.a. Clean, Hexagonal, Ports-and-Adapters
VictorRentea.ro69
Hide the persistence!
Clean your Logic!
VictorRentea.ro
public Employee getById(String employeeId) {
String sql = "SELECT id, name, phone FROM employee WHERE id = ?";
Connection conn = null;
try {
conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, employeeId);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
Employee employee = new Employee();
employee.setId(rs.getString(1));
employee.setName(rs.getString(2));
employee.setPhone(rs.getString(3));
return employee;
}
} catch (SQLException e) {
// TODO: something, don't know what..
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO: something, don't know what..
}
}
}
return null;
} Plain Old JDBC (’90-style)
70
Hide the persistence!
Clean your Logic!
VictorRentea.ro
public Employee getById(String employeeId) {
String sql = "SELECT id, name, phone FROM employee WHERE id = ?";
Connection conn = null;
try {
conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, employeeId);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
Employee employee = new Employee();
employee.setId(rs.getString(1));
employee.setName(rs.getString(2));
employee.setPhone(rs.getString(3));
return employee;
}
} catch (SQLException e) {
// TODO: something, don't know what..
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO: something, don't know what..
}
}
}
return null;
}
Plain Old JDBC (’90-style)
public Employee getById(String employeeId) {
return jdbcTemplate.queryForObject(
"SELECT id, name, phone FROM employee WHERE id = ?",
new RowMapper<Employee>() {
public Employee mapRow(ResultSet rs, int rowNum)
throws SQLException {
Employee employee = new Employee();
employee.setId(rs.getString(1));
employee.setName(rs.getString(2));
employee.setPhone(rs.getString(3));
return employee;
}
},
employeeId);
} Spring’ JdbcTemplate (or similar)
71
Hide the persistence!
Clean your Logic!
VictorRentea.ro
public Employee getById(String employeeId) {
String sql = "SELECT id, name, phone FROM employee WHERE id = ?";
Connection conn = null;
try {
conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, employeeId);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
Employee employee = new Employee();
employee.setId(rs.getString(1));
employee.setName(rs.getString(2));
employee.setPhone(rs.getString(3));
return employee;
}
} catch (SQLException e) {
// TODO: something, don't know what..
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO: something, don't know what..
}
}
}
return null;
}
Plain Old JDBC (’90-style)
public Employee getById(String employeeId) {
return jdbcTemplate.queryForObject(
"SELECT id, name, phone FROM employee WHERE id = ?",
new RowMapper<Employee>() {
public Employee mapRow(ResultSet rs, int rowNum)
throws SQLException {
Employee employee = new Employee();
employee.setId(rs.getString(1));
employee.setName(rs.getString(2));
employee.setPhone(rs.getString(3));
return employee;
}
},
employeeId);
}
Spring’ JdbcTemplate (or similar)
public interface IOrderRepository {
Employee getEmployeeBasicById(int id);
}
<select id="getEmployeeBasicById" parameterType="int" resultType="Employee">
SELECT id, name, phone_number AS phoneNumber
FROM EMPLOYEES
WHERE ID = #{id}
</select>MyBatis DataMapper
72
Hide the persistence!
Clean your Logic!
VictorRentea.ro
public Employee getById(String employeeId) {
String sql = "SELECT id, name, phone FROM employee WHERE id = ?";
Connection conn = null;
try {
conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, employeeId);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
Employee employee = new Employee();
employee.setId(rs.getString(1));
employee.setName(rs.getString(2));
employee.setPhone(rs.getString(3));
return employee;
}
} catch (SQLException e) {
// TODO: something, don't know what..
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO: something, don't know what..
}
}
}
return null;
}
Plain Old JDBC (’90-style)
public Employee getById(String employeeId) {
return jdbcTemplate.queryForObject(
"SELECT id, name, phone FROM employee WHERE id = ?",
new RowMapper<Employee>() {
public Employee mapRow(ResultSet rs, int rowNum)
throws SQLException {
Employee employee = new Employee();
employee.setId(rs.getString(1));
employee.setName(rs.getString(2));
employee.setPhone(rs.getString(3));
return employee;
}
},
employeeId);
}
Spring’ JdbcTemplate (or similar)
class IOrderRepository {
Employee getEmployeeBasicById(int id);
}
<select id="getEmployeeBasicById" parameterType="int" resultType="Employee">
SELECT id, name, phone_number AS phoneNumber
FROM EMPLOYEES
WHERE ID = #{id}
</select>
MyBatis DataMapper
public Employee getById(String id) {
List<Employee> list = em.createQuery(
"SELECT e from Employee e where e.id=:id", Employee.class)
.setParameter("id", id)
.getResultList();
if (list.isEmpty()) {
return null;
} else {
return list.get(0);
}
} JPA/Hibernate
73
Hide the persistence!
Clean your Logic!
VictorRentea.ro
public Employee getById(String employeeId) {
String sql = "SELECT id, name, phone FROM employee WHERE id = ?";
Connection conn = null;
try {
conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, employeeId);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
Employee employee = new Employee();
employee.setId(rs.getString(1));
employee.setName(rs.getString(2));
employee.setPhone(rs.getString(3));
return employee;
}
} catch (SQLException e) {
// TODO: something, don't know what..
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO: something, don't know what..
}
}
}
return null;
}
Plain Old JDBC (’90-style)
public Employee getById(String employeeId) {
return jdbcTemplate.queryForObject(
"SELECT id, name, phone FROM employee WHERE id = ?",
new RowMapper<Employee>() {
public Employee mapRow(ResultSet rs, int rowNum)
throws SQLException {
Employee employee = new Employee();
employee.setId(rs.getString(1));
employee.setName(rs.getString(2));
employee.setPhone(rs.getString(3));
return employee;
}
},
employeeId);
}
Spring’ JdbcTemplate (or similar)
class IOrderRepository {
Employee getEmployeeBasicById(int id);
}
<select id="getEmployeeBasicById" parameterType="int" resultType="Employee">
SELECT id, name, phone_number AS phoneNumber
FROM EMPLOYEES
WHERE ID = #{id}
</select>
MyBatis DataMapper
public Employee getById(String id) {
List<Employee> list = em.createQuery(
"SELECT e from Employee e where e.id=:id", Employee.class)
.setParameter("id", id)
.getResultList();
if (list.isEmpty()) {
return null;
} else {
return list.get(0);
}
}
JPA/Hibernate
@Repository
public interface EmployeeRepository
extends JpaRepository<Employee, Integer>,EmployeeRepositoryCustom {
public Employee getById(Integer employeeId);
public Optional<Employee> getByName(String name);
public Long countByName(String name);
@Query("SELECT e FROM Employee e LEFT JOIN FETCH e.projects")
public List<Employee> getAllFetchProjects();
}
Spring Data JPA - our (fine) selection
74
Hide the persistence!
Clean your Logic!
VictorRentea.ro75
Hide the persistence!
Clean your Logic!
VictorRentea.ro76
Hide the persistence!
Clean your Logic!
VictorRentea.ro77
IRepository
Repository
implementation
it’s a better,
Cleaner, Testable
world
Order
Repo
implements
calls
DIP
Hide the persistence!
Clean your Logic!
1 repo/Entity
Split them
when they grow
domain
repo
VictorRentea.ro78
Façade
DTO
Validator
Mapper
VOEntityid
Domain
Service
Domain
Service
IRepo
Repo
implem
F
application
infra
IExtSrv
Adapter ExtSrv
Adapter
WS
Interface
DTO
VictorRentea.ro79
Façade
DTO
Validator
Mapper
VOEntityid
Domain
Service
Domain
Service
IRepo
Repo
implem
F
application
IExtSrv
Adapter ExtSrv
Adapter
WS
Interface
DTO
domain
Pragmatic
for S/M apps, 2 modules might be enough
VictorRentea.ro
api
80
Façade
DTO
Validator
Mapper
VOEntityid
Domain
Service
Domain
Service
IRepo
Repo
implem
F
application
infra
IExtSrv
Adapter ExtSrv
Adapter
WS
Interface
DTO
IFacade
Sent to remote Java clients
persisting knowledge…That’s all I got on architectures
VictorRentea.ro83
Driving Principles – KISS
Modeling Data – Enemy data
Organizing Logic – Extract when it Grows
Clean Architecture – The Onion
Tests. Fear.
Agenda
VictorRentea.ro
@
VictorRentea.ro
Lots of Unit Tests are Good !
84
A sense of confidence
Developer Courage
Continuous Refactoring
(It’s still a single )
VictorRentea.ro85
As you fight to write Unit Tests,
the Production code gets simpler
@
VictorRentea.ro
Simpler
Decoupled
Live-documented
86
Testable code == Good code
@
VictorRentea.ro87
First couple of unit tests – The most valuable
Test no.
difficulty
@
VictorRentea.ro
Fast tests, Fast feedback
A. Input-output
Pure functions, no side effects. Ideally, no dependencies !
B. Domain logic by mocking dependencies
Less readable tests
C. Queries on a fresh database for each test (in-memory)
Reuse complex test data -> Fragile tests ?
88
𝑓 𝑥, 𝑦 = 𝑥2 + 𝑦2
examples
JavaScript
Unit Tests?
@
VictorRentea.ro
Prod:Test code size
1:3 – 1:10
Short, Readable, Clean tests
@Test(expected = ValidationException.class)
public void withoutNameFails() {
validator.validate(aValidCustomer().withName(null).build());
}
Write Unit Test
is HARD !
@
VictorRentea.ro90
@
VictorRentea.ro
Disclaimer
91
Experience in
Banking, Flavors, ERP and Posting
domains
Mostly Backend Guy
Open-minded Client
(Freedom to architecture)
Projects of size
S-L
VictorRentea.ro92
Driving Principles –
Agenda
VictorRentea.ro
KISS
Modeling Data –
Organizing Logic –
Clean Architecture –
Tests. Fear.
Extract when it Grows
The Onion
Enemy data
VictorRentea.ro93
Driving Principles –
VictorRentea.ro
KISS
Modeling Data –
Organizing Logic –
Clean Architecture –
Tests. Fear.
Extract when it Grows
The Onion
Enemy data
AgendaTakeaways
VictorRentea.ro94 VictorRentea.ro
Tests. Fear.
Organizing Logic – Extract when it Grows
Clean Architecture – The Onion
Modeling Data – Enemy data
KISS: Avoid overengineering
Magic to protect your Developers
Takeaways
VictorRentea.ro95 VictorRentea.ro
Tests. Fear.
Organizing Logic – Extract when it Grows
Clean Architecture – The Onion
Enemy data
KISS: Avoid overengineering
Magic to protect your Developers
in your DTOs: keep them out
Takeaways
VictorRentea.ro96 VictorRentea.ro
Tests. Fear.
Extract when it Grows
Clean Architecture – The Onion
KISS: Avoid overengineering
Magic to protect your Developers
: for SRP or DRY
Enemy data in your DTOs: keep them out
Takeaways
VictorRentea.ro97 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)
VictorRentea.ro98 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)
VictorRentea.ro99
KISS
VictorRentea.ro100
Keep It simple
101
How to
apply all
this in my
legacy
code
??
Where
can I read
more ?
Show us
more
code !!
©
@
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”
Further Reading
102
@
VictorRentea.ro103
HUGE Thanks
to the JPoint Reviewers !!
Vladimir Sitnikov
Anton Arhipov
Enterprise Java Training
VictorRentea.ro
victor.rentea@gmail.com
@victorrentea
© Copyright Victor Rentea 2017
Victor Rentea
22 feb 2017
Brainstorming a
Clean Pragmatic Architecture

Clean Pragmatic Architecture - Avoiding a Monolith