Delivering large, complex software rapidly, frequently and reliably requires a loosely coupled organization. DevOps teams should rarely need to communicate and coordinate in order to get work done. Conway's law states that an organization and the architecture that it develops mirror one another. Hence, a loosely coupled organization requires a loosely coupled architecture.
In this presentation, you will learn about design-time coupling in a microservice architecture and why it's essential to minimize it. I describe how to design service APIs to reduce coupling. You will learn how to minimize design-time coupling by applying a version of the DRY principle. I describe how key microservices patterns potentially result in tight design time coupling and how to avoid it.
6. @crichardson
Microservice architecture
= architectural style
Online store
createOrder(…)
Restaurant
Service
Order
Service
REST API
API
Gateway
Small team
Loosely coupled
Independently deployable
<15 minute lead time
Restaurant team
Order team
findOrder(…)
findOrderHistory(…)
7. @crichardson
Why microservices: success triangle
Process: Lean + DevOps/Continuous Delivery & Deployment
Organization:
Network of small,
loosely coupled, teams
Architecture:
Loosely coupled
Microservices
(sometimes)
IT must deliver software
rapidly, frequently, reliably and
sustainably
Enables:
Loose
coupling
Enables:
Testability
Deployability
Businesses must be
nimble, agile and
innovate faster
S/W
VUCA
8. @crichardson
Order Service
Customer Service
Operations that span services generate
coupling
Create
Order()
Order Subdomain
Customer Subdomain
reserveCredit()
createOrder()
Customer
Order
Coupling
Generate
degree of
connectedness
9. Runtime coupling impacts
availability
Order
Service
Customer
Service
POST /orders
1 PUT /customers/id
Response
4
2
3
Order
Service
Customer
Service
POST /orders
1
4
2
3
Order Created event
Credit Reserved Event
Tight: lower availability
Loose: higher availability
ID
partial Outcome
ID
Outcome
10. Design time coupling impacts
productivity
The degree to which service A is
forced to change in lock step with
service B
Caused by direct, indirect and implicit
dependencies
Lockstep changes:
Coordination between teams
Reduced productivity
Changes to Customer Service affect
Order Service
Rarely - Loose coupling
Often - Tight coupling
API
Order
Service
Customer
Service
reserveCredit()
createOrder()
Change
Change
Change
12. Order Service
Ideally: no coupling between
services
Order
Subdomain
Customer
Subdomain
Too
big?
API Gateway
… Service
CreateOrder()
… Service
Required Required
In practice: collaboration is unavoidable need to minimize coupling
14. @crichardson
Modularity and loose coupling
is an old idea
Customer Service
Order Service
Order Service
«Subdomain»
Orders
«Subdomain»
Customers
VS. «Subdomain»
Orders
«Subdomain»
Customers
«Subdomain» Orders
«Aggregate»
Order
«Subdomain» Customers
«Aggregate»
Customer
«Subdomain» Orders
«Aggregate»
Order
«Aggregate»
Customer
VS.
create()
reserveCredit()
releaseCredit()
name
creditLimit
availableCredit
«Aggregate»
Customer
create()
name
«Aggregate»
Customer
reserveCredit()
releaseCredit()
creditLimit
availableCredit
«Aggregate»
CustomerCredit
VS.
15. 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”
….
Loose design-time coupling/modularity
=
16. @crichardson
Lock-step change: adding a
COVID delivery surcharge
interface OrderService {
Order getOrder()
…
}
class Order
…
Money subtotal
Money tax
Money serviceFee
Money deliveryFee
…
}
No explicit
total
Money covidSurcharge
New!
Order
Service
Accounting
Service
…
Service
…
Service
Update
Calculate Calculate Calculate
Calculate
17. @crichardson
Accounting
Service
Cross-team change: monolith
vs. microservices
Service
Accounting
Subdomain
Order
Subdomain
Order
Service
Accounting
Subdomain
Order
Subdomain
1. Change
2. Change
3. Build
4. Test
5. Deploy
1. Change
3. Build
4. Test
5. Deploy
3. Build
4. Test
5. Deploy
V2 API
Straightforward
Complicated
2. Change
1. Change
2. Change
V1 API
19. @crichardson
DRY (Don't repeat yourself)
services
"Every piece of
knowledge must have
a single, unambiguous,
authoritative
representation within a
system"
https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
For example:
Order Total
20. Shared library that calculates Order
Total != DRY
Shared libraries containing
business logic that changes
requires multiple services
to change/rebuild/
redeployed in lock step ❌
Shared utility libraries ✅
Service A
Library
Service B
Library
Service …
Library
Change
21. @crichardson
DRY: calculate Order Total in
the Order Service
interface OrderService {
Order getOrder()
…
}
class Order
…
Money tax
Money serviceFee
Money deliveryFee
…
}
Money total
Order
Service
Accounting
Service
…
Service
…
Service
Update
Calculate
22. @crichardson
Icebergs: expose as little as possible
Implementation
API
Small, stable
API
Large, complex
implementation Develop
Hidden design
decisions
Twilio: sendSms(from, to, message)
Easier to
change
More difficult to
change
24. Consumer
Consume as little as possible
Minimize
number of dependencies
what’s consumed from each
dependency
Apply Postel’s Robustness principle:
https://en.wikipedia.org/wiki/
Robustness_principle
Consumer-driven contract tests verify
compliance
BTW: Swagger/Protobuf-generated
stubs parse everything!
{
…
"tax": …
"serviceFee": …
"deliveryFee": …
"total": "12.34"
…
}
class Order {
Money total;
}
What you ignore can’t affect you
27. @crichardson
Create Order: orchestration-based saga
API
Gateway
Restaurant
Service
Order Service
Kitchen
Service
Accounting
Service
Consumer
Service
createOrder()
ValidateConsumer
Restaurant*
AuthorizeCard
CreateTicket
Order
subtotal()
tax()
total()
Restaurant
ID
•Manages Order
•Calculates subtotal
•Calculates taxes,
fees, etc.
•Knows menus
31. @crichardson
Solution: Move some responsibilities from
Order Service to Restaurant Service
Restaurant Service Order Service
• Knows menus
• Knows line items
• Calculates subtotal
Order Validation
ok?
subtotal
• Calculates taxes,
fees, etc.
32. @crichardson
Use API Composition to display a Ticket
Kitchen Service
Restaurant Service
API
Gateway
getTicket()
getOrder()
getTicket()
35. @crichardson
Summary
Rapid and frequent development requires loose design-time
coupling
You must carefully define your services to achieve loose
coupling
Apply the DRY principle
Design services to be icebergs
Carefully design service dependencies
Avoid sharing database tables