Rapid, reliable, frequent and sustainable software development requires an architecture that is loosely coupled and modular.
Teams need to be able complete their work with minimal coordination and communication with other teams.
They also need to be able keep the software’s technology stack up to date.
However, the microservice architecture isn’t always the only way to satisfy these requirements.
Yet, neither is the monolithic architecture.
In this talk, I describe loose coupling and modularity and why they are is essential.
You will learn about three architectural patterns: traditional monolith, modular monolith and microservices.
I describe the benefits, drawbacks and issues of each pattern and how well it supports rapid, reliable, frequent and sustainable development.
You will learn some heuristics for selecting the appropriate pattern for your application.
2. @crichardson
Presentation goal
Why is rapid, frequent, reliable and sustainable
delivery of software essential?
What does that mean for software architecture?
Compare and contrast architectural patterns:
Monolithic architecture and Microservice
architecture
4. @crichardson
Agenda
Architectural requirements for rapid, frequent, reliable and
sustainable delivery of software
Defining an architecture
The monolithic architecture is not an anti-pattern
The microservice architecture pattern
6. @crichardson
Successful applications live for a long time, e.g. 10 years,
20 years, …
BUT
Technology changes: Programming languages,
frameworks, …
Time
Technology A Technology B
V1 V2 V3 V…
Importance
The importance of a technology changes over time
Relying on technology that is unimportant to market
Not using technology that is important to the market
Murer, Stephan,Bruno Bonati Consulting, Bruno Bonati. Managed Evolution: A Strategy for Very Large Information Systems
7. About software architecture
“The software architecture of a computing system is the
set of structures needed to reason about the system,
which comprise software elements, relations among
them, and properties of both.”
Documenting Software Architectures, Bass et al
9. Development in high performing
organizations
“Complete their work without communicating and
coordinating with people outside their team”
“Make large-scale changes to the design of their system
without depending on other teams to make changes in
their systems or creating significant work for other teams”
“We can do most of our testing without requiring an
integrated environment.”
“We can and do deploy or release our application
independently of other applications/services it depends
on.”
10. Required architectural quality
attributes (a.k.a. -ilities)
“Complete their work without
communicating and coordinating with
people outside their team”
“Make large-scale changes to the design
of their system without depending on
other teams to make changes in their
systems or creating significant work for
other teams”
“We can do most of our testing without
requiring an integrated environment.”
“We can and do deploy or release our
application independently of other
applications/services it depends on.”
Loosely coupled
(Conway’s law)
Modular
Testable
Deployable
API encapsulates design decisions
13. @crichardson
Agenda
Architectural requirements for rapid, frequent, reliable and
sustainable delivery of software
Defining an architecture
The monolithic architecture is not an anti-pattern
The microservice architecture pattern
14. @crichardson
How to define an
Architecture…
Application
≪subdomain≫
Customer
management
≪aggregate≫
Customer
≪subdomain≫
Order
management
≪aggregate≫
Order
createCustomer()
createOrder()
findOrder()
findOrderHistory()
System operations
Distill
Requirements
Event storming
Functional requirements
As a consumer
I want to place an Order
So that I can ….
As a Restaurant
I want to accept an Order
So that I can ….
User stories
• SLA: Reliability/Latency
• Scalability
• …
System quality attributes
The “requests” that the
application implements
Customer Team
Order Team
About Subdomains
• Business capability/function/etc
• Logical view: packages and classes
• Team-sized
• Loosely coupled (Conways law)
1
2
15. @crichardson
Application
… how to define an
Architecture
Application
≪subdomain≫
Customer
≪aggregate≫
Customer
≪subdomain≫
Order
≪aggregate≫
Order
createCustomer()
createOrder()
findOrder()
findOrderHistory()
System operations
Component
≪subdomain≫
≪subdomain≫
≪subdomain≫
≪subdomain≫
How to group
subdomains
into
components*?
Component Component
* Component = deployable, executable, e.g. WAR file, executable JAR, …
3
Deployable, Executable,
e.g. WAR file, executable
JAR, …
16. @crichardson
Grouping subdomains into
components: together or separate?
≪subdomain≫
Customer
≪aggregate≫
Customer
≪subdomain≫
Order
≪aggregate≫
Order
Attraction
Repulsion
Generated by
system operations
Simple components
Team-sized services
Fast deployment pipeline
…
Dark energy: an anti-
gravity that’s accelerating
the expansion of the
universe
Dark matter: an invisible
matter that has a
gravitational effect on stars
and galaxies.
https://www.nasa.gov/feature/goddard/2020/new-hubble-data-explains-missing-dark-matter
Simple interactions
Prefer ACID or BASE
Minimize runtime coupling
…
https://chrisrichardson.net/post/microservices/2021/04/15/mucon-2021-dark-energy-dark-matter.html
17. @crichardson
Repulsive forces subdomains
in different services
https://chrisrichardson.net/post/microservices/2021/04/15/mucon-2021-dark-energy-dark-matter.html
Service
Service
«Subdomain» A
«Aggregate»
X
«Subdomain» B
«Aggregate»
Y
Simple components
Team autonomy
Fast deployment pipeline
Support multiple technology stacks
Cost effective scaling
Segregate regulated software
Segregate highly available components
18. @crichardson
Attractive forces
subdomains in same service
https://chrisrichardson.net/post/microservices/2021/04/15/mucon-2021-dark-energy-dark-matter.html
System Operation()
Service
Service
«Subdomain» A
«Aggregate»
X
«Subdomain» B
«Aggregate»
Y
Simple interactions
Prefer ACID over BASE
Minimize runtime coupling
Efficient inter-service communication
Minimize design time coupling
19. @crichardson
The role of the architect is
balance conflicting forces
Minimize attraction
Service
Subdomain
Subdomain
Minimize
repulsion
Service
Subdomain
Subdomain
Minimize
repulsion
Conflicting goals
20. @crichardson
Choice of architectural styles
Application
Service
≪subdomain≫
≪subdomain≫
≪subdomain≫
≪subdomain≫
Service
Service
Application
≪subdomain≫
≪subdomain≫
≪subdomain≫
≪subdomain≫
Monolithic architecture:
One component
Microservice architecture:
Many components
Resolves
Potentially neglects
Potentially neglects
Resolves
21. @crichardson
Agenda
Architectural requirements for rapid, frequent, reliable and
sustainable delivery of software
Defining an architecture
The monolithic architecture is not an anti-pattern
The microservice architecture pattern
22. @crichardson
Starting development of a monolithic
application
$ mkdir application
$ cd application
$ mkdir -p web/src/main domain/src/main
persistence/src/main
$ vi build.gradle
$ mkdir application
$ cd application
$ curl -G
https://start.spring.io/starter.zip
-o app.zip
$ unzip app.zip
OR
23. @crichardson
Monolithic architecture
Github Repository
«Gradle Project»
FtgoApplication
«Gradle Subproject»
main
main
«Gradle Subproject»
web
web
.customers
web.orders
«Gradle Subproject»
domain
domain.
customers
domain.
orders
«Gradle Subproject»
persistence
persistence.
customers
persistence.
orders
Customer team
Order team
Deployment pipeline
Production
FTGO
Application
Executable JAR
Technical
layers
24. @crichardson
Monolithic architecture: benefits and
drawbacks
Repulsive forces
Simple components
Team autonomy
Fast deployment pipeline
Support multiple technology stacks
Cost effective scaling
Segregate regulated software
Segregate highly available components
Attractive forces
Simple interactions ✅
Prefer ACID over BASE ✅
Minimize runtime coupling ✅
Efficient inter-service communication ✅
Minimize design time coupling ✅
✅ or ❌ ∝1/ size
✅ or ❌ ∝1/ size
✅ or ❌ ∝1/ size
❌
❌
❌
✅ if role-based deployments
26. @crichardson
Large scale development
challenges: git pull/push
$ git clone
….
$ git commit
$ git pull
$./gradlew precommitTests
$ git push
Time consuming?
Time consuming?
All of them?
Rejected?
use partial
/shallow clones
Use merge queue
27. @crichardson
Stateless, ephemeral, e.g. CI-aaS
Large scale development
challenges: 15 minute lead time
Deployment pipeline
Compile
Download
dependencies
Start
application
Run tests
lead time ∝ application size
Significant Really significant
28. @crichardson
Accelerating the deployment
pipeline with technology
Cache downloaded dependencies between builds
Incremental builds - class level dependencies
Compilation avoidance - skip recompile if changed dependency is
binary compatible
Distributed build cache: reuse task output from other/previous build if
inputs have not changed
Parallelization
Single machine - e.g. Gradle
Cluster - e.g. Bazel
30. @crichardson
Github Repository
«Gradle Project»
FtgoApplication
«Gradle Subproject»
main
main
«Gradle Subproject»
web
web
.customers
web.orders
«Gradle Subproject»
domain
domain.
customers
domain.
orders
«Gradle Subproject»
persistence
persistence.
customers
persistence.
orders
Customer team
Order team
Deployment pipeline
Production
FTGO
Application
Executable JAR
Re-examining the architecture
One or many
One or many per repository
Horizontal layers vs vertical slices
31. @crichardson
Github Repository
«Gradle Project»
FtgoApplication
«Gradle Subproject»
main
main
«Gradle Subproject»
orders
orders.web
«Gradle Subproject»
customerAPI
orders.
domain
«Gradle Subproject»
customers
customers.
persistence
orders.
persistence
Customer team
Order team
Deployment pipeline
Production
FTGO
Application
Executable JAR
customers.
domain
customers.
web
customers.
api
Modular monolith: domain-oriented
Defines API
Vertical slices/domains
Clear ownership
Single large repo
Slow or complicated
32. @crichardson
About the Customer API
module
Service-style facade
Promotes loose coupling/
Encapsulation
Example: define using
Protobuf!!
Accelerates builds
Test Order module with mock
Customer module
Changing Customer
implementation module won’t
trigger rebuild/test of its
clients
Order
Customer
API
Customer
Main
X
33. @crichardson
How modular can you go:
Transaction boundaries?
Benefits Drawbacks
Per-request
• Simple implementation (ACID)
• Efficient data access
• Tight transaction coupling
between modules
• More difficult to migrate to
services
Per module/
aggregate
• Loose transaction coupling
between modules
• Easier to migrate to services
Potentially:
• More complex
implementation (BASE),
e.g. Sagas
• Inefficient data access
34. @crichardson
How modular can you go:
interaction styles?
Traditional inter-module communication, e.g.
Method call that takes parameters and returns some values
Caller waits for method to return/not throw exception
Risk of tight runtime coupling if module was refactored to a
service
Consider using asynchronous style of communication: events,
async request/response, one-way commands, …
35. @crichardson
How modular should you go?
Transaction per module
Asynchronous inter-module communication
=
Many of the costs/complexities of microservices without the
benefits
YAGNI - https://martinfowler.com/bliki/Yagni.html ?
36. @crichardson
Modular monolith using multi repos
Enforces
consistent set
of 3rd party
dependencies
across all
projects
Defines API
Clear ownership
Multiple, smaller
Simpler, faster
Potentially slow/
complicated
Maven
Repository
Github Repository
Github Repository
Github Repository
Github Repository
«Gradle project»
main
main
«Gradle project»
orders
orders.web
«Gradle project»
customerAPI
orders.
domain
«Gradle project»
customers
customers.
persistence
orders.
persistence
Customer team
Order team
Deployment pipeline
Production
FTGO Application
Executable JAR
customers.
domain
customers.
web
customers.
api
Orders
JAR
Deployment pipeline
Customers
API
JAR
Deployment pipeline Customers
JAR
Deployment pipeline
Github Repository
«Gradle project»
BOM/Platform Deployment pipeline
BOM
Artifact
37. @crichardson
Application-level deployment
pipeline
Assembles released binary versions of modules to create component, e.g. executable
JAR
Must validate/test the application/component
Key issue is composability: the modules work independently but do they work together?
Dependency conflicts: NoSuchMethodError
Spring bean name conflicts
When are these errors detected:
“build time” - ideally, e.g. verify set of transitive dependencies is meaningful
On startup - potentially ok
Request handling => requires running potentially slow tests
38. @crichardson
Monolithic architecture: benefits and drawbacks
revisited
Repulsive forces
Simple components ✅ or ❌ ∝1/ size
Team autonomy ✅ or ❌ ∝1/ size
Fast deployment pipeline ✅ or ❌ ∝1/ size
Support multiple technology stacks ❌
Cost effective scaling ✅ if role-based deployments
Segregate regulated software ❌
Segregate highly available components ❌
Attractive forces
Simple interactions ✅
Prefer ACID over BASE ✅
Minimize runtime coupling ✅
Efficient inter-service communication ✅
Minimize design time coupling ✅
Modularization + Technology
enable the Monolithic Architecture to be
used for larger applications
BUT
the application can ultimately outgrow its
architecture when these forces cannot be
resolved
It can’t resolve these forces
39. @crichardson
Agenda
Architectural requirements for rapid, frequent, reliable and
sustainable delivery of software
Defining an architecture
The monolithic architecture is not an anti-pattern
The microservice architecture pattern
40. @crichardson
Microservice architecture:
Many components
Production
Github Repository
Github Repository
«Gradle project»
orders
orders.web
orders.
domain
«Gradle project»
customers
customers.
persistence
orders.
persistence
Customer team
Order team
Deployment
pipeline
«Executable JAR»
Order
Service
customers.
domain
customers.
web
customers.
main
orders.main
«Executable JAR»
Customer
Service
Deployment
pipeline
«Service Instance»
Order
Service
«Service Instance»
Customer
Service
Inter-service
Communication
Clear ownership
Multiple small
Simpler, faster Per-service path
to production
45. @crichardson
Microservice architecture: Benefits and drawbacks
Repulsive forces
Simple components ✅
Team autonomy ✅
Fast deployment pipeline ✅
Support multiple technology stacks ✅
Cost effective scaling ✅
Segregate regulated software ✅
Segregate highly available components ✅
Attractive forces
Simple interactions ❌
Prefer ACID over BASE ❌
Minimize runtime coupling ❌
Efficient inter-service communication ❌
Minimize design time coupling ❌
Must group
subdomains to:
• Maximize benefits
• Minimize drawbacks
Potential benefits
Potential drawbacks
46. @crichardson
The challenge = attractive and
repulsive forces conflict
≪Complicated≫
Order Mgmt
≪Simple≫
Customer Mgmt
≪Complicated≫
Fulfillment
findOrder()
createOrder()
Order Team
Fulfillment
Team
Customer
Team
getOrder()
createOrder()
reserveCredit()
getDelivery()
scheduleDelivery()
49. @crichardson
Balancing forces: simplifying
createOrder()
Order Team
Fulfillment
Team
Customer
Team
Fulfillment
Service
Order
Service
≪Complicated≫
Order
Mgmt
≪Simple≫
Customer
Mgmt
≪Complicated≫
Fulfillment
Reduced autonomy 😢
Fulfillment
Service
Order
Service
createOrder()
Order Created
Simple
notification 😀
Still complicated 😢
50. Microservices drawbacks: BASE is
inherently more complex than ACID
ACID transactions
Free rollback
Databases implement Isolation (I)
Isolation: concurrent execution equivalent to some sequential execution
e.g. claim locks that are held until commit
BUT
Typically need compensating transactions
Sagas are ACD - Lack of I
Each step of a saga is a transaction
Concurrent execution of sagas => Potential data anomalies
Analyze carefully and select countermeasures = design techniques that provide I
51. @crichardson
Microservices drawbacks: APIs
are more complicated
interface OrderService {
…
}
vs.
$ javac
Use an inter-service communication
library/framework
object.method(args)
Runtime checking = risk of outages
Use consumer-driven contract tests to
prevent breaking changes
vs.
vs.
Partial failure, latency, …
Complete failure vs.
52. @crichardson
Application
Deployment pipeline
Microservices drawbacks: brittle, slow, costly
end to end tests
Order Service deployment
pipeline
Customer Service
deployment pipeline
End to End
tests
Production
Given a customer with an availableCredit of $100
And a product costing $10
When the customer orders the product
Then an order is created
And the availableCredit becomes $90
Traditional acceptance test
Spans
services
Bottleneck: increases lead time, MTTR
53. @crichardson
Push acceptance tests down the pyramid
Order Service deployment
pipeline
Customer Service
deployment pipeline
Production
Given a customer with sufficient credit
And a product costing $10
When the customer orders the product
Then an order is created
And credit was reserved
Refactored acceptance test
Given a customer with an availableCredit of $100
When the customer reserves $10
Then the availableCredit becomes $90
Refactored acceptance test
54. @crichardson
Run acceptance tests in
production
Production
Given a customer with an availableCredit of $100
And a product costing $10
When the customer orders the product
Then an order is created
And the availableCredit becomes $90
Traditional acceptance test
55. Order Service
V1
Order Service
V2
Test deployed code before
releasing: e.g. Canary release
1.Deploy V2 alongside V1
2.Test V2
3.Route test traffic to V2
4.Release V2 to a small % of production
users
5.Monitor/test (latency, errors) - undo
rollout if errors
6. Increase % of production traffic going
to V2
7.Repeat until 100% of traffic going to V2
8.Eventually undeploy V1
Intelligent
traffic router
Order Service
V1
Order Service
V2
Monitoring system
57. @crichardson
Summary
Architect constantly
Evolve your architecture as your requirements change
Spectrum of architectural patterns:
Monolith
Layered monolith
Modular monolith
Microservices
Pick the pattern than balances dark energy and dark matter