Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Clean Pragmatic Architecture - Avoiding a Monolith

11,710 views

Published on

Talk built based on several of my trainings: http://www.victorrentea.ro/#training
Covers: Clean/Onion/Hexagonal Architecture, Domain Entities, Value Objects, Repository, Extract When it Grows Principle, Dependency Inversion Principle, Clean Code and Design Patterns.
These are the backing slides of the talks given at JPoint 2017 and Devoxx PL 2017: https://www.youtube.com/embed/4-4ahz7zDiQ

Published in: Software
  • Be the first to comment

Clean Pragmatic Architecture - Avoiding a Monolith

  1. 1. 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..?
  2. 2. 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.
  3. 3. 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.
  4. 4. 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.
  5. 5. 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.
  6. 6. 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?
  7. 7. 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);
  8. 8. 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.
  9. 9. 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.
  10. 10. 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.
  11. 11. Enterprise Java Training VictorRentea.ro victor.rentea@gmail.com @victorrentea © Copyright Victor Rentea 2017 Victor Rentea 22 feb 2017 Brainstorming a Clean Pragmatic Architecture
  12. 12. @ 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
  13. 13. VictorRentea.ro13 Driving Principles – KISS Modeling Data – Enemy data Organizing Logic – Extract when it Grows Clean Architecture – The Onion Tests. Fear. Agenda VictorRentea.ro
  14. 14. @ VictorRentea.ro Single Responsibility Principle 14 EmployeeManager -read/persist -compute pay-roll -generate PDF report -manage projects vs
  15. 15. @ VictorRentea.ro Coupling 15 vs Read more: https://dzone.com/articles/probably-the-best-package-structure-in-the-world
  16. 16. @ VictorRentea.ro Don’t Repeat Yourself 16
  17. 17. @ VictorRentea.ro Keep It Short & Simple 17 Premature encapsulation is the root of all evilOverengineering – Adam Bien
  18. 18. @ VictorRentea.ro Premature encapsulation is the root of all evilOverengineering Keep It Short & Simple 18
  19. 19. @ VictorRentea.ro Keep It Short & Simple 19 Premature encapsulation is the root of all evilOverengineering Less, simple code Developer Happiness 
  20. 20. @ VictorRentea.ro Invisible Magic to reduce effort and risk Protect the Developers 20 ... Avoid (yet another) Custom Framework (to learn)  Developer Comfort
  21. 21. VictorRentea.ro21 Request/Thread Scope @Autowired private MyRequestContext requestContext; ... { entity.setModifiedBy(requestContext.getCurrentUser()); } @Component @Scope(value = "request", proxyMode = TARGET_CLASS) class MyRequestContext { ... }
  22. 22. @ VictorRentea.ro Fear Kills Creativity Simplify Unit Testing - Strong regression defense Protect the Developers 22 Developer Safety
  23. 23. @ VictorRentea.ro Always Think Regular Brainstorming 23 Regular Refactoring
  24. 24. @ VictorRentea.ro24 Today, 7 April 2017, I stopped refactoring. Today, my application became Legacy
  25. 25. @ 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
  26. 26. VictorRentea.ro26 Driving Principles – KISS Modeling Data – Enemy data Organizing Logic – Extract when it Grows Clean Architecture – The Onion Tests. Fear. Agenda VictorRentea.ro
  27. 27. @ VictorRentea.ro You control them! Entities hold your persistent data 27 Entity Logic Use them to simplify the implementation of your domain logic
  28. 28. @ 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 }
  29. 29. @ 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
  30. 30. @ 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();
  31. 31. @ 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)
  32. 32. @ 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…
  33. 33. VictorRentea.ro33 Driving Principles – KISS Modeling Data – Enemy data Organizing Logic Clean Architecture – The Onion Tests. Fear. Agenda VictorRentea.ro
  34. 34. @ 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);
  35. 35. @ VictorRentea.ro Mappers go to DB ? KISS: Yes ! 35
  36. 36. @ 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
  37. 37. @ VictorRentea.ro37 N+1 Queries x 1000
  38. 38. @ 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
  39. 39. 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)
  40. 40. VictorRentea.ro40 Measure, Don’t Guess® Premature encapsulation is the root of all evil – Adam Bien optimization – Donald Knuth Performance ?
  41. 41. @ 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)
  42. 42. @ 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)
  43. 43. @ 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
  44. 44. @ 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
  45. 45. @ 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
  46. 46. 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
  47. 47. 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”
  48. 48. @ 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 ?!!
  49. 49. @ 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
  50. 50. @ VictorRentea.ro Continuous Practice Pair Programming
  51. 51. @ VictorRentea.ro Developer Comfort is essential for Emerging Architectures
  52. 52. @ VictorRentea.ro52
  53. 53. VictorRentea.ro53 Driving Principles – KISS Modeling Data – Enemy data Organizing Logic – Extract when it Grows Clean Architecture – The Onion Tests. Fear. Agenda VictorRentea.ro
  54. 54. VictorRentea.ro54 VOEntityid Domain Service Domain Service What code would you protect? Put it in the domain module Priceless Domain Logic Domain Objects
  55. 55. VictorRentea.ro Façade DTO Validator Mapper F application 55 VOEntityid Domain Service Domain Service domain All the other code depends on domain
  56. 56. VictorRentea.ro56 External Service DTO JAXB JSON Domain Service Domain Service domain
  57. 57. VictorRentea.ro57 External Service DTO JAXB JSON Domain Service Domain Service domain
  58. 58. VictorRentea.ro ExtServ Adapter 58 External Service DTO JAXB JSON Domain Service Domain Service domain
  59. 59. 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 . . . . .
  60. 60. 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
  61. 61. 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
  62. 62. 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
  63. 63. VictorRentea.ro infra ExtServ Adapter 63 External Service DTO JAXB JSON Protect your Domain Model! Domain Service IExtServ Adapter <dependency> implements domain
  64. 64. VictorRentea.ro infra ExtServ Adapter 64 External Service DTO JAXB JSON IExtServ Adapter <dependency> implements domain
  65. 65. VictorRentea.ro infra ExtServ Adapter 65 External Service DTO JAXB JSON IExtServ Adapter <dependency> implements HTTP, RMI, … FTP JMS domain DB
  66. 66. 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
  67. 67. 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
  68. 68. 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
  69. 69. VictorRentea.ro69 Hide the persistence! Clean your Logic!
  70. 70. 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!
  71. 71. 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!
  72. 72. 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!
  73. 73. 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!
  74. 74. 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!
  75. 75. VictorRentea.ro75 Hide the persistence! Clean your Logic!
  76. 76. VictorRentea.ro76 Hide the persistence! Clean your Logic!
  77. 77. 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
  78. 78. 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
  79. 79. 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
  80. 80. 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
  81. 81. VictorRentea.ro83 Driving Principles – KISS Modeling Data – Enemy data Organizing Logic – Extract when it Grows Clean Architecture – The Onion Tests. Fear. Agenda VictorRentea.ro
  82. 82. @ VictorRentea.ro Lots of Unit Tests are Good ! 84 A sense of confidence Developer Courage Continuous Refactoring (It’s still a single )
  83. 83. VictorRentea.ro85 As you fight to write Unit Tests, the Production code gets simpler
  84. 84. @ VictorRentea.ro Simpler Decoupled Live-documented 86 Testable code == Good code
  85. 85. @ VictorRentea.ro87 First couple of unit tests – The most valuable Test no. difficulty
  86. 86. @ 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?
  87. 87. @ 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 !
  88. 88. @ VictorRentea.ro90
  89. 89. @ 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
  90. 90. VictorRentea.ro92 Driving Principles – Agenda VictorRentea.ro KISS Modeling Data – Organizing Logic – Clean Architecture – Tests. Fear. Extract when it Grows The Onion Enemy data
  91. 91. VictorRentea.ro93 Driving Principles – VictorRentea.ro KISS Modeling Data – Organizing Logic – Clean Architecture – Tests. Fear. Extract when it Grows The Onion Enemy data AgendaTakeaways
  92. 92. 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
  93. 93. 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
  94. 94. 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
  95. 95. 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)
  96. 96. 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)
  97. 97. VictorRentea.ro99 KISS
  98. 98. VictorRentea.ro100 Keep It simple
  99. 99. 101 How to apply all this in my legacy code ?? Where can I read more ? Show us more code !! ©
  100. 100. @ 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
  101. 101. @ VictorRentea.ro103 HUGE Thanks to the JPoint Reviewers !! Vladimir Sitnikov Anton Arhipov
  102. 102. Enterprise Java Training VictorRentea.ro victor.rentea@gmail.com @victorrentea © Copyright Victor Rentea 2017 Victor Rentea 22 feb 2017 Brainstorming a Clean Pragmatic Architecture

×