Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clean, Pragmatic Architecture at Virtual JUG

Clean, Pragmatic Architecture at Virtual JUG

In a mood for a closing brainstorm? Let’s have a critical review of the major decisions taken in a typical enterprise application architecture and learn to balance pragmatism with design goals. Find out how to do just-in-time design to keep as many use-cases as simple as possible (KISS).

We’ve all seen that without continuous refactoring simplistic design eventually degenerates into a Big Ball of Mud, under the assault of new features and bugfixes. At the other end, over-engineered code can burden the start-up of the development and then end up freezing the code in some rigid ‘a-priori’ design. It’s up to us to strike a balance that will preserve Developer Happiness™. To do that, we should regularly challenge the architecture of our system and seek ways to simplify it to fit our present needs, with a pragmatic mindset.

“Architecture is the art of postponing decisions”, said Uncle Bob. This session takes this idea further and explains how to Evolve a Pragmatic, Clean Architecture (aka Onion), guiding the design by what Genetic Programming would call a fitness function. It’s the philosophy Victor applied to design and develop 9 applications for IBM, and a key topic of the trainings he delivered at dozens of companies.

Along the way, you’ll also get a nice review of the fundamental data structure types, how to keep the logic simple by two types of extractions, enforcing boundaries using dependency directions, and crafting a testable design, all in a fun, dynamic and interactive session.

Victor Rentea

March 28, 2019
Tweet

More Decks by Victor Rentea

Other Decks in Programming

Transcript

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

    Do It More Often! "Bring The Pain Forward!"
  2. Victor Rentea 14 years of Java Clean Code Evangelist VictorRentea.ro

    30+ talks, 12 meetups .NET Lead Architect Tech Team Lead and Consultant Software Craftsman XP: Pair Programming, Refactoring, TDD
  3. VictorRentea.ro @victorrentea [email protected] Independent Technical Trainer & Coach Hibernate Spring

    Java 8 Architecture, DDD Design Patterns Clean Code Unit Testing, TDD Java Performance and much more… Scala 180+ days 1300 devs 6 years VictorRentea.ro [email protected] 30 companies Posting daily on
  4. 30 Keep It Short & Simple Premature encapsulation is the

    root of all evil Overengineering – Adam Bien
  5. 31 Keep It Short & Simple Premature encapsulation is the

    root of all evil Overengineering Simpler code Developer Happiness
  6. 32 Invisible Magic to reduce effort and risk Protect the

    Developers ... Avoid building an intrusive Custom Framework (bugs to learn) Developer Comfort
  7. 33 Request/Thread Scope @Autowired private MyRequestContext requestContext; ... { entity.setModifiedBy(requestContext.getCurrentUser());

    } @Component @Scope(value = "request", proxyMode = TARGET_CLASS) class MyRequestContext { ... } Request Metadata
  8. Fear Kills Creativity Simplify Unit Testing - Strong regression defense

    Protect the Developers 34 Developer Safety
  9. 36

  10. 39 Keep Struggling for a Can you predict the future

    ? What features will grow super-complex in time? Simple Design At all times!
  11. 40 - Kent Beck https://martinfowler.com/bliki/BeckDesignRules.html 2. Reveals Intention 3. No

    Duplication 4. Fewest Elements 1. Passes the Tests Rules of Simple Design
  12. 44 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 }
  13. 45 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 Put small bits of highly reusable domain logic in your Domain Entities
  14. Entity id 46 Small Immutable Lombok? No persistent ID unlike

    an Entity Equal by value of all fields Embeddable In larger 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();
  15. Then, you start exposing data to UI (SPA, WS, JSF,

    desktop) UI has different goals They want data structures to match their screens. Never expose your Entities in your API 47 isDeletable: true (to show/hide the Delete button)
  16. I like dumb DTOs (you'll see soon why) public fields

    ?! ! !.. VO Entity id 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; } } 48 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();
  17. 50 Complex Conversion? Extract Mappers VO Entity id 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);
  18. 61 Start implementing all domain logic in a Facade Mapper

    VO Entity id DTO Facade Facade Domain Service Domain Service Domain Services This approach is detailed in Java EE Patterns - Rethinking Best Practices, by Adam Bien Then extract logic into -To hide complexity: SRP -For Reuse: DRY ▪ (across Facades or Services) ≈ Application Service [DDD]
  19. 63 Keep DTOs out! (parameters and return types) Mapper VO

    Entity id 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 methods)
  20. Fç Validate Data Façade Convert Data Implement Logic Aspects -

    Transactions - Logging - Exception Handling* - Access Control* 64 Facade Roles DTO Validator Mapper Domain Service VO Entity id
  21. 65 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 summarize 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”
  22. 66 CustomerFacade saveCustomer() getCustomer() searchCustomer() saveCustomerPreferences() getCustomerPreferences() validateAddress() resetPassword() checkPassworStrength()

    CustomerPreferecesFacade saveCustomerPreferences() getCustomerPreferences() validateAddress() resetPassword() checkPassworStrength() CustomerFacade saveCustomer() getCustomer() searchCustomer() Extract when it Grows at the same level of abstraction Horizontal Splitting > 300 lines
  23. AlertService CompositeOrder Service 67 Separation by Layers of Abstraction OrderService

    DeliveryService Extract when it Grows Vertical Extraction more abstract, more higher-level
  24. 69 Duplication Pair Programming Imposed repeat in .js the validations

    from .java Rush/Lazy Inadvertent you forget and implement it again Inter-developer you didn't knew that was already implemented bus factor =2
  25. 71

  26. 74

  27. 76 VO Entity id Domain Service Domain Service What code

    would you protect? Put it in the domain module Priceless Domain Logic Domain Objects domain
  28. Façade DTO Validator Mapper F application 77 domain depends on

    domain VO Entity id Domain Service Domain Service
  29. 79 External Service DTO Adapter Domain Service Domain Service <dependency>

    domain infrastructure A huge Adapter® Pattern - Hide their ugly API - Handle their Exceptions - Decouple their DTOs  your Entities - Validate their evil data
  30. 80 External Service DTO IAdapter Adapter implements 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
  31. 81 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
  32. 84

  33. 86 VO Entity id 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 Priceless Domain Logic Domain Objects
  34. infra ExtSrv Adapter Repo implem 87 Façade DTO Validator Mapper

    VO Entity id Domain Service Domain Service IExtSrv Adapter IRepo F application The Onion Architecture Behold, a.k.a. Clean, Hexagonal, Ports-and-Adapters
  35. 98 Façade DTO Validator Mapper VO Entity id Domain Service

    Domain Service IRepo Repo implem F application infra IExtSrv Adapter Adapter WS Interface DTO
  36. 99 Façade DTO Validator Mapper VO Entity id Domain Service

    Domain Service IRepo Repo implem F application IExtSrv Adapter Adapter WS Interface DTO domain Pragmatic for decent apps, 2 modules are enough
  37. Use-case Optimal Query 100 CALLS DEPENDS Breaking into Bounded Contexts

    DTO Facade Infra Tip: Measure boundary violations com.myapp.order. … com.myapp.product. … EVENT TX FK? SELECT MICRO… "Light CQRS" consistency
  38. 102

  39. 109 Lots of Unit Tests are Good ! A sense

    of confidence Developer Courage Continuous Refactoring (It’s still a single )
  40. Maintainable Tests Pure Functions No side effects. No dependencies. Mock-based

    Less readable In-memory Emulation or JPQL tests on H2/* db Fragile data fixtures On Real Systems Eg: Connecting to a real DB 113 , = 2 + 2
  41. ▪ 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 ▪ “Measure Don’t Ask” is TM of Kirk Pepperdine Further Reading 121
  42. 124 VictorRentea.ro Tests. Fear. Organizing Logic The Onion Architecture Modeling

    Data KISS: Avoid overengineering Magic to protect your Developers Takeaways
  43. 125 VictorRentea.ro Tests. Fear. Organizing Logic The Onion Architecture Enemy

    data KISS: Avoid overengineering Magic to protect your Developers in your DTOs: keep them out Takeaways
  44. 126 VictorRentea.ro Tests. Fear. Extract when it Grows The Onion

    Architecture KISS: Avoid overengineering Magic to protect your Developers : for SRP or DRY Enemy data in your DTOs: keep them out Takeaways
  45. 127 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)
  46. 128 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)