The document discusses several cases where architecture decisions hurt software teams. It provides lessons learned from each case:
- Overly sharing code across domains can cause dependencies and fragility. Architectural boundaries should separate domains.
- Attempting to satisfy all use cases with a single highly configurable solution often satisfies no one. Specific solutions may be preferable.
- Microservices are not a universal solution and will not improve a flawed design alone. Modules should only be split across microservices at domain boundaries.
- Systems need to be designed to evolve dynamically over time as requirements change. Centralized control hinders this.
2. Agenda
• Architector rolle and Architecture definitions
• Cases, when architecture decisions hurt team (*)
– Context
– Observations (what happens)
– Solution and lesson learned
• Conclusion
* Examples was taken from VMWare, TYMIQ GmbH and INNOQ projects
3. About Me
• Software Engineer at VMWare Tanzu Labs
• Committer in Spring and Apache projects
• Speaker at technical conferences
4. Architecture Definitions
⟨system⟩ fundamental concepts or
properties of a system in its
environment embodied in its
elements, relationships, and in the
principles of its design and evolution
(ISO 42010)
Architecture represents the significant
design decisions that shape a system,
where significant is measured by cost
of change." -- Grady Booch.
Architecture is anything that team
considers important enough for
the system development and
evolving
5. Architecture is NOT:
Upfront activity performed by somebody in charge of telling
everyone else what to do
It is no a description or document, it is a property of your system
or even: it is your system (intentional or accidental)
12. Online Shop Design
Customer Domain Article Domain Order Domain
User Service Cart Service Article Service Order Service
SAP
Messaging
REST REST REST REST
Core Domain
DAOs
DB DB DB
SAP Connector Messaging Connector
13. Online Shop Design
Customer Domain Article Domain Order Domain
User Service Cart Service Article Service Order Service
SAP
ActiveMQ
REST REST REST REST
Core Domain
DAOs
DB DB DB
SAP Connector Messaging Connector
14. • Modification and maintenance of common domains
is hard
• Deployment in single container causes dependencies
and fragility between teams
• Migrations to new versions of Java, Hibernate, CXF
are extremely difficult and took very long time
Observation
15. Step 1: Split Codebase due Domain Code Duplication
User Customer
Domain
Checkout
Article Domain
Order Domain
User Service Cart Service Checkout Service Order Service
SAP
Messaging
REST REST REST REST
User Core
Domain
DAOs
DB
Container
Cart Customer
Domain
Cart Core
Domain
Core Order
Domain
Cart Article
Domain
Checkout Core
Domain
Core
framework as
thridparty
16. 16
Step 2: Extract Services with High Availability Requirements
Gateway
Old Service2
inside Monolith
Price&Availability
Microservice
Monolith container
Consumer 1 Consumer 2 Consumer 3
Routing / Canary release
90% 10%
Old Service1
inside Monolith
Old Service3
inside Mnolith
• Request load is essentially higher
(or lower) as rest monolith
components
17. Step 4: Decentralize Data Management
Docker
Order Service
REST
R
E
S
T
Docker
Checkout
Service
REST
R
E
S
T
Docker
Cart Service
REST
R
E
S
T
Docker
User Service
REST
R
E
S
T
SAP
Messaging
DB
DB
DB DB
18. • Think twice before share module, especially across
bounded contexts
• Duplication is less critical as common dependencies
• Treat common utils dependencies as third party libraries
and make them as thin as possible
• Making decisions, system evolving and maintenance
inside team is a lot of easier as a cross teams
Lessons Learned
20. Context
• E-Commerce (retail) provider
• Global customer base
• Platform for the clients that served their clients
• Catalog/CMS/Shop/Fulfilment
• Multi-tenant
• Highly customizable
22. • If you attempt to satisfy everyone, you will likely end
up to satisfying no one
• Specific solution is often more preferable as highly
configurable one
Lessons Learned
26. • Legacy technology
• Code difficult to support and maintain
• Hard to extend for new payment providers
Problems reported by customer
27. • Split to microservices to make code maintainable
• Microservices architecture will help to make architecture
more extensible
Initial plan from customer
28. • There is only single bounded context
• Both modules service and job belong to this bounded
context
• There is single business domain model
• Splitting to other modules / microservices doesn’t make
sense at all and can make problems even worse
Analyse shows
29. • Service and Job were rewritten using TDD and pair
programming
• Both modules were migrated to SpringBoot, Spring Batch was
used for Job to get monitoring out of the box
• Huge amount of optimizations were done
• Both modules shared the same database and data model
Final Solution
30. • Unnecessary splitting of modules belonging in the same
bounded context can cause more problems in
architecture
• Microservices is not universal solution and wouldn’t
improved bad design automatically
• The main reasons of splitting are: business subdomain,
scalability requirements, lifecycle, failure isolation,
different technology stack
Lessons Learned
31. #4 Your system WILL be dynamic
• Large scale insurance system
• Model driven development
• 2 releases per year
• > 100 developers (more maintain)
32.
33.
34.
35. Lessons learned
• Centralized responsibity hurts
• Faced too much rigidity, people always find a way around
the rules
51. Symtpoms
• Ritual inclusion of code or program structures that serve no
real purpose (redundant checks for null values, testing
whether a collection is empty before iterating )
• Copying existing code with little understanding of how it
works
• Applying a design pattern or coding style blindly without
understanding the reasons behind that design principle
• Trying to make system too flexible: adopting different types
of databases, configuration sources, communication
protocols even if they do not really needed
52. Context
• Framework to discover, configure and secure web
services
• Customer is flight modelling company
• 10 developers
53. Design
Database
Repository Layer
Model Layer
Configuration
Layer
API Layer
Configuration technical
service
Repository
Repository Layer
Wiring Layer
Artifact Layer
API Layer
Service Registry
SQL, Non-SQL DBs
Impl 2, 3
File, DB, Git
Other repos
Other wirings
Impl 2, 3
Mapping
Mapping
Mapping
Mapping
Mapping
Mapping
… …
Communication
abstraction
Communication
abstraction
http://
Queue
54. Lessons learned
• Abstractions in system design are often leaky
• Build new layers carefully: not because of fashion, coolness or
dreams
• Be pragmatic for now, but predict system evolving
59. • Any feature requires communication between the teams
• One group should wait for another one
• If backend systems are unresponsive, frontend teams
quickly becomes secret „full stack“
70. Lessons learned
• Smart endpoints, dumb pipes (even with cool names)
• Value of specific over generic solutions
71. Takeaways
• Don‘t be afraid about architecture
• Choose the simplest thing that will work
• Create evolvable structures, manage architecural evolution
• Don‘t built road blocks, create value and get out of the way
78. But …
• Luck of standartization led to ineffecient UI integration at
runtime (integration was done at hock)
• Vast differences in API style, formats, documentation created
needless extra work (json+hal, json+saron)
• Despite not centralized frontend, centralized frontend team
creates new bottleneck (centrailzed tool chain)
79. You cannot decide to not have an architecture; if you don‘t
actively create it, be prepare to deal with the one what emerges
There is a fine line between diversity (that adds value) and chaos
(that doesn‘t)
Extremely loose coupling requires a few rules, but they need to
be enforced strictly