@crichardson
Minimizing Design-time Coupling
in a Microservice Architecture
Chris Richardson
Microservice architecture consultant and trainer
Founder of Eventuate.io
Founder of the original CloudFoundry.com
Author of POJOs in Action and Microservices Patterns
@crichardson
chris@chrisrichardson.net
http://adopt.microservices.io
Copyright Š 2021. Chris Richardson Consulting, Inc. All rights reserved
@crichardson
What you will learn
What is design-time coupling?
What problems does it create?
How to design loosely coupled
services?
@crichardson
About Chris
http://adopt.microservices.io
@crichardson
Discounts
35% discount
ctwqcon21
$120 discount coupon
HIDKQIFC
http://adopt.microservices.io
@crichardson
Agenda
• Microservices and design-time coupling
• Minimizing design-time coupling
• Takeout burritos: a case study in design-time coupling
@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(…)
@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
@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
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
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
@crichardson
Loose coupling is NOT
guaranteed
You must design your services
to be loosely coupled
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
@crichardson
Agenda
• Microservices and design-time coupling
• Minimizing design-time coupling
• Takeout burritos: a case study in design-time coupling
@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.
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
=
@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
@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
@crichardson
@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
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
@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
@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
@crichardson
What to encapsulate?
Ancient wisdom from Parnas!
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
Use a database-per-service
Order
Service
Customer
Service
Database
Customer
table
Tight design-
time/runtime
coupling
Order
Service
Customer
Service
Order database
Order
table
Customer database
Customer
table
APIs
only
Order
table
@crichardson
Agenda
• Microservices and design-time coupling
• Minimizing design-time coupling
• Takeout burritos: a case study in design-time coupling
@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
@crichardson
Restaurant Service
Order Service
Exposes
MenuItems
Consumes
MenuItems
Makes
assumptions
about
MenuItems
•Knows menus
•Calculates subtotal
•Knows menus
@crichardson
Scenario: Different sizes of
chips and salsa
Small
Large + $3.50
UI
getRestaurant()
Restaurant*
parentMenuItemId
@crichardson
Widespread impact!!
Scenario: Customized burritos
Restaurant
Service
Order
Service
Kitchen
Service
@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.
@crichardson
Use API Composition to display a Ticket
Kitchen Service
Restaurant Service
API
Gateway
getTicket()
getOrder()
getTicket()
@crichardson
Choreography-based coordination
API
Gateway
Restaurant Service
Order Service
Kitchen Service
OrderValidated(subtotal)
OrderValidationFailed()
Order Creation
Requested
Order
Ticket
Order
createOrder()
@crichardson
Orchestration-based coordination
API
Gateway
Restaurant Service
Order Service
Kitchen Service
Order
Ticket
Order
createOrder()
Orchestrator
subtotal
createOrder(….)
createOrder(….)
createTicket(….)
@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
@crichardson
@crichardson chris@chrisrichardson.net
http://adopt.microservices.io
Questions?
ctwqcon21 HIDKQIFC

QConPlus 2021: Minimizing Design Time Coupling in a Microservice Architecture

  • 1.
    @crichardson Minimizing Design-time Coupling ina Microservice Architecture Chris Richardson Microservice architecture consultant and trainer Founder of Eventuate.io Founder of the original CloudFoundry.com Author of POJOs in Action and Microservices Patterns @crichardson chris@chrisrichardson.net http://adopt.microservices.io Copyright Š 2021. Chris Richardson Consulting, Inc. All rights reserved
  • 2.
    @crichardson What you willlearn What is design-time coupling? What problems does it create? How to design loosely coupled services?
  • 3.
  • 4.
    @crichardson Discounts 35% discount ctwqcon21 $120 discountcoupon HIDKQIFC http://adopt.microservices.io
  • 5.
    @crichardson Agenda • Microservices anddesign-time coupling • Minimizing design-time coupling • Takeout burritos: a case study in design-time coupling
  • 6.
    @crichardson Microservice architecture = architecturalstyle 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: successtriangle 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 Operationsthat 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 couplingimpacts 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
  • 11.
    @crichardson Loose coupling isNOT guaranteed You must design your services to be loosely coupled
  • 12.
    Order Service Ideally: nocoupling between services Order Subdomain Customer Subdomain Too big? API Gateway … Service CreateOrder() … Service Required Required In practice: collaboration is unavoidable need to minimize coupling
  • 13.
    @crichardson Agenda • Microservices anddesign-time coupling • Minimizing design-time coupling • Takeout burritos: a case study in design-time coupling
  • 14.
    @crichardson Modularity and loosecoupling 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 highperforming 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: addinga 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
  • 18.
  • 19.
    @crichardson DRY (Don't repeatyourself) 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 thatcalculates 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 OrderTotal 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 aslittle 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
  • 23.
  • 24.
    Consumer Consume as littleas 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
  • 25.
    Use a database-per-service Order Service Customer Service Database Customer table Tightdesign- time/runtime coupling Order Service Customer Service Order database Order table Customer database Customer table APIs only Order table
  • 26.
    @crichardson Agenda • Microservices anddesign-time coupling • Minimizing design-time coupling • Takeout burritos: a case study in design-time coupling
  • 27.
    @crichardson Create Order: orchestration-basedsaga 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
  • 28.
  • 29.
    @crichardson Scenario: Different sizesof chips and salsa Small Large + $3.50 UI getRestaurant() Restaurant* parentMenuItemId
  • 30.
    @crichardson Widespread impact!! Scenario: Customizedburritos Restaurant Service Order Service Kitchen Service
  • 31.
    @crichardson Solution: Move someresponsibilities 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 Compositionto display a Ticket Kitchen Service Restaurant Service API Gateway getTicket() getOrder() getTicket()
  • 33.
    @crichardson Choreography-based coordination API Gateway Restaurant Service OrderService Kitchen Service OrderValidated(subtotal) OrderValidationFailed() Order Creation Requested Order Ticket Order createOrder()
  • 34.
    @crichardson Orchestration-based coordination API Gateway Restaurant Service OrderService Kitchen Service Order Ticket Order createOrder() Orchestrator subtotal createOrder(….) createOrder(….) createTicket(….)
  • 35.
    @crichardson Summary Rapid and frequentdevelopment 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
  • 36.