17 years of code, code, code, code, code.... Consultant & Trainer: 5000 developers of 100+ companies in EU: ❤ Clean Code, Architecture, Unit Tes3ng 🛠 Spring, Hibernate, Reactor ⚡ Java Performance, Secure Coding 🔐 Conference Speaker – many conferences talks on YouTube Founder of European SoLware CraLers (5500+ members) 🔥 Free monthly webinars, 1 hour a9er work 👉 victorrentea.ro/community Past events on my channel: youtube.com/vrentea Father of 👧👦, host of a 🐈, weekend gardener 🌼 h"ps://VictorRentea.ro
Full of dirty fixes Slow and risky to change Craving for refactoring, but lacking unit tests The architecture of many successful systems in produc@on today aka Big Ball of Mud
more likely to suffer from internal coupling (ie. domain dilu6on, scope creep) than from coupling to external APIs and Libraries Elders Wisdom Focus of tradi6onal Concentric Architectures (Clean, Onion, Hexagonal, Ports-Adapters)
from scratch, re-specifying all behavior - New features in old system: delay or implement twice - 20-30% success rate - Likely to end up with a Distributed Monolith 😱 2) Strangler-Fig Pa4ern - Implement new features in fresh microservices - Con6nuously strip bits of func6onality from the monolith 3) Refactor to Modules > Extract 🌈 - Decouple logic and data in the same monolithic codebase From a Legacy Monolith💀 to Microservices🌈 Highly-coupled Distributed Systems
as complexity accumulates, produc6vity starts to drop overhead of managing distributed systems reduces ini6al produc6vity the decreased coupling of microservices outweighs their governance overhead Produc2vity years Monolith But I'm going to build a complex system!
project with microservices, even if you're sure your application will be big enough to make it worthwhile." - Martin Fowler h-ps://marKnfowler.com/bliki/MonolithFirst.html (2015) Start simple and address the natural bottlenecks of the system (functional/technical).
the best of both worlds? Physical architecture of a Monolith Logical architecture of Microservices Ability to easy extract a Microservice out when necessary
ApplicaGon Catalog Microservice Catalog DB Orders Microservice Orders DB REST REST/MQ Payment Microservice Payment DB Customer Microservice Customer DB vs
the complexity Pricing Catalog Customer • customer first name • customer last name • product name • product description • customer status • product price class Customer { firstName lastName status !!... } class Product { name description price !!... }
Just Behavior Example: calcula6on, valida6on • Just CRUD Data Example: Country, Tenant, Clinic * the same applies for "Microservice" Microservices: configura6on or en6ty microservice CountryRepo in the 'shared' module
test on CI public void independentSubdomains() { slices().matching("!..myapp.(*)!..*") .should().notDependOnEachOther() .ignoreDependency(resideInAnyPackage( "!..shared!..", "!..api!..")) .check(new ClassFileImporter(). importPackages("com.myapp")); } 2) Communica2on between Modules The internals of a module should never be accessed from another module Each Module exposes a Public API that other modules can reference Public API events calls 1. Compiler-enforced Separate CompilaGon Units (eg Maven modules) 2. Unit Tests on CI Code Analysis, eg ArchUnit: Part of the Public API can be exposed as External API to remote apps (eg via REST, MQ) External API
part of the Public API J Decoupled: publisher unaware of listeners (poten@ally mul@ple) J Poten@ally async J Poten@ally persisted L Not suitable for request-reply interac@on L Naviga@ng code is harder (than a method call) Event Types: No@fica@on {id}, or Heavy-Events {state} events Module CommunicaJon via Events / Messages
orchestrator Keep Orchestra@on Up Just fix the build, allow the ó calls A B A-api B-api Extract API modules run'me calls Facade keep a coarse grained API, eg for a monolith site Fixing Cyclic Dependencies between Modules microservices: N/A microservices: events on queue microservices: interdependent microservices microservices: api gateway/bff/saga microservices: shared lib/3rd service microservices: merge? ❤ AB Merge Modules Tight Coupling (eg dozens of links) wrong boundaries? Using Dependency Inversion B can let A implement logic, without depending on A A B ç event call Publish Events (in-mem) ⭐Decoupled A B C commons Push Commons Down A B implements è call Dependency Inversion ⭐Decoupled AProviderImpl runKme call AProvider Module A must react to changes in B Technical Stuff Only send email countryRepo audit, ... uSls
module is responsible for its own data ⚠ A Module should not query the tables of another module (breaks encapsula@on) Instead, ask for that data via the Public API
IsolaGon: Any module can freely read/write any table Write IsolaGon: A table is updated by ONE module, but can be read by other modules Table IsolaGon⭐: A module has exclusive access to its tables (usually separate schemas) Consistency IsolaGon - tables of different modules: (a) cannot share a Foreign Key, and (b) are never updated in the same DB transac@on Separate database instance / module Different database types, eg PostgresSQL / MongoDB Before extrac@ng a microservice
are easier in a monolith Module Tests Integra0on tes0ng an Module in isola0on - mock public APIs of other modules - mock event bus è Spring Modulith project h4ps://spring.io/projects/spring-modulith
Invest more @me to clarify module boundaries Ø Consider merging highly-coupled modules Ø Don't sacrifice consistency too early Ø Carefully design how data is shared between modules Ø Don't leak internal classes via module's Public API Ø Monitor architecture fitness func@ons using code analysis tools
existing large-scale banking platform monolith that needs to split into microservices, but isn't ready to separate into completely independent services. •A greenfield implementation eg. a new peer-to-peer lending platform in the early stages of growing into a full suite of financial services •A system with a low-moderate scale eg. a movie ticket booking platform that serves several thousand users and handles a few million transactions per week. •Non-complex software platforms eg. a notes/documents syncing and management platform for consumer apps. When to choose a Modulith? When to move to Microservices? •High Delivery Rate ? eg. the company decides investing in shipping to self-collect boxes on the street, and new versions are deployed weekly. •Scalability eg. the endpoint selecting the ad to insert in a live stream expected to take 1k-10k requests/second. •Fault-tolerance eg. in the same ad-insert solution, if the management part fails, the ads still have to be inserted •Different Programming Language eg. the senior developers of sales department are using C# while the rest of the teams use Java. Oh, and Kotlin rocks!🤘 •Security eg. customer management has to be audited for GDPR compliance regarding PII information.
the Team will outweigh any monolith/microservice choice A great team will build a great project whatever architectural style you choose #investInPeople
more? Register for my upcoming deep dive webinar on Architectures on May 29-30 at h"ps://clean.re For a company workshop see victorrentea.ro/training-offer
"It works > Ship it now, refactor later" for 5-25 years § Domain-Driven Design (Eric Evans, 2003) link - Design Rich Domain Model for complex problems; - Split soluKon in Bounded Context § Onion/Clean Architecture (Jeffrey Palermo, Uncle Bob 2008) link - Keep core Domain Logic agnosKc from UI, APIs, DB, frameworks (by dep. inversion) § PragmaQc Onion link - Merge applicaKon with infrastructure, evolve layers as needed, tolerate ORM on Domain Model § Microservices (2011) - Loosely coupled, independent business capabiliKes deployed separately § Distributed Monolith☠ link - Tightly coupled microservices due to premature, too fine-grained breakdown § VerQcal Slice Architecture (Jimmy Bogard, 2018) link - Group code by feature, not by technical layer § Modular Monolith, aka Modulith (Simon Brown, 2018) - Implement business capabiliKes as loosely coupled modules in a single deployment unit Overview of Architecture Styles