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. View Slide

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

    View Slide

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

    View Slide

  4. When something is painful
    but you can't avoid doing it…
    Do It More Often!
    "Bring The Pain Forward!"

    View Slide

  5. Continuous Integration
    Pair Programming
    Continuous Refactoring
    TDD
    XP
    "Bring The Pain Forward!"

    View Slide

  6. 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

    View Slide

  7. 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

    View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. 26
    The most important principle
    in programming?

    View Slide

  13. 27
    Single Responsibility Principle
    EmployeeManager
    -read/persist
    -compute pay-roll
    -generate PDF report
    -manage projects
    vs

    View Slide

  14. 28
    Coupling
    vs

    View Slide

  15. 29
    Don’t Repeat Yourself

    View Slide

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

    View Slide

  17. 31
    Keep It Short & Simple
    Premature encapsulation is the root of all evil
    Overengineering
    Simpler code Developer Happiness

    View Slide

  18. 32
    Invisible Magic to reduce effort and risk
    Protect the Developers
    ...
    Avoid building an intrusive
    Custom Framework
    (bugs to learn)
    Developer
    Comfort

    View Slide

  19. 33
    Request/Thread Scope
    @Autowired
    private MyRequestContext requestContext;
    ... {
    entity.setModifiedBy(requestContext.getCurrentUser());
    }
    @Component
    @Scope(value = "request", proxyMode = TARGET_CLASS)
    class MyRequestContext { ... }
    Request
    Metadata

    View Slide

  20. Fear Kills Creativity Simplify Unit Testing
    - Strong regression defense
    Protect the Developers
    34
    Developer
    Safety

    View Slide

  21. Always Think
    Regular Brainstorming
    35
    Continuous Refactoring
    There are NO boring tasks
    automate them

    View Slide

  22. 36

    View Slide

  23. 39
    Keep Struggling for a
    Can you predict the future ?
    What features will grow
    super-complex in time?
    Simple Design
    At all times!

    View Slide

  24. 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

    View Slide

  25. 41
    A Simple Architecture
    that Evolves
    to Match
    the Growing Complexity
    This talk is about…

    View Slide

  26. 42
    Core Principles
    Modeling Data
    Organizing Logic
    The Onion Architecture
    Tests. Fear.
    Agenda
    VictorRentea.ro

    View Slide

  27. You control them!
    Entities = your persistent data
    43
    Entity
    They simplify your logic

    View Slide

  28. 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
    }

    View Slide

  29. 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 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

    View Slide

  30. 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();

    View Slide

  31. 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)

    View Slide

  32. 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();

    View Slide

  33. 49
    Core Principles
    Modeling Data
    Organizing Logic
    The Onion Architecture
    Tests. Fear.
    Agenda

    View Slide

  34. 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);

    View Slide

  35. 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]

    View Slide

  36. 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)

    View Slide


  37. 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

    View Slide

  38. 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”

    View Slide

  39. 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

    View Slide

  40. AlertService
    CompositeOrder
    Service
    67
    Separation by Layers of Abstraction
    OrderService DeliveryService
    Extract when it Grows
    Vertical Extraction
    more abstract,
    more higher-level

    View Slide

  41. 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

    View Slide

  42. Practice
    Pair Programming

    View Slide

  43. 71

    View Slide

  44. Developer Comfort
    is essential for
    Emerging Architectures

    View Slide

  45. 74

    View Slide

  46. 75
    Core Principles
    Modeling Data
    Organizing Logic
    The Onion Architecture
    Tests. Fear.
    Agenda
    VictorRentea.ro

    View Slide

  47. 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

    View Slide

  48. Façade
    DTO
    Validator
    Mapper
    F
    application
    77
    domain
    depends on domain
    VO
    Entity
    id
    Domain
    Service
    Domain
    Service

    View Slide

  49. 78
    External
    Service
    DTO
    Domain
    Service
    Domain
    Service
    domain

    View Slide

  50. 79
    External
    Service
    DTO
    Adapter
    Domain
    Service
    Domain
    Service

    domain infrastructure
    A huge Adapter® Pattern
    - Hide their ugly API
    - Handle their Exceptions
    - Decouple their DTOs  your Entities
    - Validate their evil data

    View Slide

  51. 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

    domain infrastructure

    View Slide

  52. 81
    calls
    Dependency Inversion Principle

    higher-level
    module
    lower-level
    module
    "Best of OOP"
    - Uncle Bob
    Abstractions should not depend on details
    Low level classes
    are not visible

    View Slide

  53. infra
    Adapter
    83
    External
    Service
    XML, JSON
    IAdapter

    implements
    HTTP, RMI, …
    FTP
    JMS
    domain
    DB
    DTO

    View Slide

  54. 84

    View Slide

  55. 85
    BUT…

    View Slide

  56. 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

    View Slide

  57. 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

    View Slide

  58. 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

    View Slide

  59. 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

    View Slide

  60. 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

    View Slide

  61. 102

    View Slide

  62. 103
    tardigrade

    View Slide

  63. 104
    fitness function

    View Slide

  64. 105
    coupling
    performance
    scalability
    fitness function
    security
    source code size

    View Slide

  65. 108
    Core Principles
    Modeling Data
    Organizing Logic
    The Onion Architecture
    Tests. Fear.
    Agenda
    VictorRentea.ro

    View Slide

  66. 109
    Lots of Unit Tests are Good !
    A sense of confidence
    Developer Courage
    Continuous Refactoring
    (It’s still a single )

    View Slide

  67. 110
    As you fight to write Unit Tests,
    the Production code gets simpler

    View Slide

  68. 112
    First couple of unit tests : The most valuable
    Test no.
    difficulty

    View Slide

  69. 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

    View Slide

  70. infrastructure
    domain
    side effects
    116
    pure logic

    View Slide

  71. ▪ 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

    View Slide

  72. 122
    Agenda
    VictorRentea.ro
    Core Principles
    Modeling Data
    Organizing Logic
    The Onion Architecture
    Tests. Fear.

    View Slide

  73. 123
    Core Principles
    VictorRentea.ro
    Modeling Data
    Organizing Logic
    The Onion Architecture
    Tests. Fear.
    Agenda
    Takeaways

    View Slide

  74. 124 VictorRentea.ro
    Tests. Fear.
    Organizing Logic
    The Onion Architecture
    Modeling Data
    KISS: Avoid overengineering
    Magic to protect your Developers
    Takeaways

    View Slide

  75. 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

    View Slide

  76. 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

    View Slide

  77. 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)

    View Slide

  78. 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)

    View Slide

  79. 129
    KISS

    View Slide

  80. 130
    Keep It simple

    View Slide

  81. 136
    Put Passion In
    All That You Do!!

    View Slide

  82. Thank You!!

    View Slide