Principles of Software Architecture Modernization : Delivering engineering excellence with the art of fixing microservices, monoliths, and distributed monoliths
Long path to better systems that last longer and make engineers and customers happier
Key Features
● Guidance, trade-o
178
12
20MB
English
Pages 474
Year 2024
Report DMCA / Copyright
DOWNLOAD EPUB FILE
Table of contents :
Cover
Title Page
Copyright Page
About the Authors
About the Reviewer
Acknowledgement
Preface
Table of Contents
1. What’s Wrong with Monoliths?
Structure
What are monoliths?
Big codebase
Few deployment units
Other monolith smells
Centralized
Old
Sidebar: Does size matter?
Issues with monoliths
Patterns in software engineering
What is an anti-pattern?
Living with Monoliths: Anti-patterns, side effects, and amplifiers
Monolith anti-patterns
High coupling
Wrong abstractions
Lack of isolation
Monolith side effects
Difficult deployments
Insufficient testing
Lack of ownership
Slow adoption of new technologies
Slow development cycle
Sidebar: Automation and anti-patterns
Amplifiers
Broken windows/copy and paste
Microservice envy
Lack of Talent Density
Fear of change
Are all monoliths bad?
Monoliths are a form of software architecture
Monolith benefits
Refactoring and change impact
Simplify some migrations
A good starting point
Simplified infrastructure
Types of monoliths
Classical, distributed, and modular monoliths
Modular monoliths: The good kind of monolith
Modular monoliths: Istio
Modular monoliths: Mobile SuperApps
Can bad monoliths be avoided?
Things to remember
2. Anti-Patterns: Lack of Isolation
Structure
What is isolation?
Isolation in software
Isolation temptations
Exposure
Existing components, BFF
Existing components, change existing endpoint
Existing service, add new endpoint
New service, share the database
Sidebar: What is a Backend for Frontend?
New service, share the data
What is event sourcing?
Event sourcing in software
Sidebar: CQRS and its relationship with ES
New service, share via API
Centralization
Benefits of centralizing engineering teams
Issues with over-centralization
Delivery bottleneck and innovation bottleneck
Centralization versus standardization for observability and deployments
Bad shared libraries
Breaking Isolation
Sharing databases
When is it ok to share databases?
Big Data sharing application databases
Hidden contracts and preserving isolation
Legacy system integrations
Shared libraries
Binary coupling
Lack of stable contracts
Pushing complexity upwards
Granularity and distribution: Side effects
Let us isolate everything
Databases
Shared libraries
Stable contracts
Sidebar: Deep modules and A Philosophy of Software Design
Operating Systems and infrastructure
Advanced isolation
Failure isolation and bulkheading
Fallbacks
UX isolation: Degradation and exploration
How to prevent isolation issues
Design reviews and tooling
Things to remember
3. Anti-Patterns: Distributed Monoliths
Structure
What is a distributed monolith?
Traits of distributed monoliths
How are distributed monoliths created?
Sharing databases across services
Shared persistence libraries
Shared contracts
Issues with distributed monoliths
Binary coupling
Temporal coupling
Push the complexity upwards
Embrace async with an Event-Driven Architecture
Vulnerabilities and security fixes
Testing
Management problems
Reasoning problems
Amplifiers
Team erosion
Software ownership
Anti-science mentality
Features and bugs
Sidebar: Landmines
Bad environments
Types of distributed monoliths
Backend
BFF and frontend
Serverless
Complexity in serverless architectures
Backfire: worse again!
Architects as gatekeepers do not scale
New capability/feature
New table
EOL technology
Stop the bleeding
Consider going back to the monolith
Team and process advice
Technology and software architecture advice
Things to remember
4. Anti-Patterns: Internal Shared Libraries
Structure
What is a library?
Types of libraries
Sidebar: Libraries versus frameworks
Issues with libraries
Constant migrations
Lack of stable contracts
Binary coupling / breaking isolation
Incentives for library creation
The Framework Anti-Pattern
In defense of libraries
Performance
Reliability path
Language idiomatic
Code centralization
Avoid reinventing the wheel
Pitfalls: Bad practices to avoid
Internal shared libraries
Short blanket effect
Big frameworks
Bloated libraries
Highly popular libraries
Libraries shipping configurations
Team erosion in shared libraries
Service drivers
Utils
Wrappers
Extension
New abstractions
Lack of governance
Better options
When should we create a library?
When should we NOT create a library?
Leverage services
No binary coupling
Much improved flexibility
Easier migrations or no migrations at all
Critical reliability path
In some cases, performance
Sidecar pattern
Modern sidecars
Sidecars and proxies
Sidecars and Kubernetes
How can you build a Sidecar? Types of Sidecars
Sidebar: Service meshes
Simple alternatives
Leverage existing SDKs and libraries
Do it yourself
Copy and paste the code
Contribute to open source
Proper library design
Duplication vs re-use
Bad duplication: Business code
Bad duplication: Compliance
Good duplication: Migrations
Good duplication: Configuration and setup code
Reuse: The double edged sword
Proper dependency management
Cherry pick dependencies
Avoid company-wide parent POMs
Explicitly declare dependencies
Lean libraries
The spectrum of options
Things to remember
5. Assessments
Structure
What is an assessment?
Why perform assessments?
Typical modernization projects
Successful modernization projects
Motivations
Assessments
Mind the Cone of Uncertainty
Technology and business needs
Modernization strategy
Decisions and tradeoffs
Build versus buy
The case to buy
The case to build
Rewrite versus refactor
The power of backward compatibility
Elements of proper assessments
Code analysis at scale
Classification and judgment
Ownership
Rate of business change
Public contracts
Downstream dependencies
Upstream dependencies
User facing?
Complexity
Pass rate / test coverage
Database analysis
Classifying the level of independence
Classical monolith
Microservices or proper services
Distributed monolith
Different persistence frameworks
Isolated schemas
Isolated tables
Domain mapping
List all the domains
Quarantine the system
Assessment outcomes
Quick wins
Prioritization, expectations, and strategy
Business Impact versus effort
Order of events
Radical change
Things to remember
6. Principles of Proper Services
Structure
Service oriented architecture
Types of services
When to use a service
When to avoid services
SOA benefits
Faster time to market
Lower costs and easier maintenance
Extensibility and adaptability
Independence
Summary of SOA benefits
Contract first
New service
Existing component
Coding contracts with OpenAPI
Backward compatibility
SOA and isolation
Isolate datastores
Isolate libraries
Sidebar: flavors versus bridges in libraries and monoliths
Isolated public contracts
Design reviews
Automating contract health
Non obvious things
Handling errors in contracts
Service exposure
Things to remember
7. Proper Service Testing
Structure
Why testing?
Correctness
Change impact
Production ready
Traits of software tests
Types of Tests
Traits of bad tests
Inconsistent failure rate
Refactoring fragility
Data dependency entanglements
Inefficient testing cycles
Constant test tweaking
Test independence
Traits of good tests
Constant success rate
Refactoring resiliency
Data dependency isolation
Direct inputs
Internal state
Fast feedback cycles
Hands-off tests
Self-contained
Testing manifesto
Testing throughout over at the end
Preventing bugs over finding bugs
Testing understanding over checking functionality
Building the best system over breaking the system
Team responsibility for quality over tester responsibility
Testing diversity
Why testing matters to software architecture
Best practices testing services
Practical stress testing
Gatling for performance testing
Test Pyramid
Strategies for testing in production
Edge router
Beta users
Live auditing / compare and discard results
Replaying traffic
Chaos Testing
Netflix Simian Army
Toxiproxy
Resiliency matrix
Internal state - advanced deep dive
Synthetic data generation
Testing interfaces
Test data setup
Mocking interfaces
Things to remember
8. Embracing New Technology
Structure
Embrace the new, with principles
On demand resources - cloud computing
Stable contracts
Proper isolation
One account per service
One account per business domain
One account for everything
Account organization impacts contract granularity
Internal shared libraries
Serverless
How the cloud impacts team organization
Pre-built capabilities - cloud services and SaaS
Relational and NoSQL datastores
AI and analytics
Isolated deployment units
Containers and Kubernetes
Cloud spectrum
Real time access to data - streaming
Apache Kafka
Kafka architecture
Kafka and CQRS / ES
ksqlDB
Efficient engineers
Modern languages
Language spectrum
JVM languages
Flexible data models
NoSQL databases
Going beyond the relational model
Lean communications - binary APIs and GraphQL
Service-to-Service communication: REST, gRPC, and GraphQL
What REST got right
Interoperability vs performance
GraphQL
gRPC
Things to remember
9. Code Migrations
Structure
Why do code migrations matter?
Inventory, use cases, and POCs
Red vs green migrations
Elements of proper code, migrations
Migration target
Difficulty
Customer impact - online vs offline
Execution - service team vs. platform team
Code migration patterns
Backward compatibility
Lazy migrations
Strangler Fig
Converting a classical monolith to proper SOA
Converting a classical monolith to a modular monolith
Converting a distributed monolith to proper SOA
Amazon two - way doors
Migration roadblocks
Roadblock - leftovers
Double down on inventories and POCs
Distributed migrations and team dependencies
Repetition, repetition, repetition
Roadblock - friction and entropy
Roadblock - high WIP limits, business pressure, compliance, and other traps
Post-migration
Rollback strategies
Things to remember
10. Data Migrations
Structure
Data migration risks
Pre-data migrations
Inventory and analysis
Planning and identifying quick wins
Data migration patterns
Revisiting - lazy migrations
Export and import
Database replication
Table diff
Triggers
Change Data Capture
Dual writing
Cold data
Failure in one write operation
Transaction delay and complexity
Data migration strategies
Online vs offline
Schema first versus multiple migrations
Revising: Strangler Fig
Classical monoliths, Strangler Fig, and data migrations
Executing migrations
Testing
Pre-migration sanity testing
Performance testing
Practical database performance testing with NDBench
Sanity test: Post-migration structure and data
Observability
Post-data migrations
Ghost hunting
Clean up and decommissioning
Things to remember
11. Epilogue
Structure
Encore!
Principles matter the most
Education
City planning
Vision
Onward
Index