No Sharing of Domain-Specific Code
Context
Microservices are in use or are being adopted. In order to improve the code reuse between microservices sharing code (e.g. as libraries) is applied or evaluated.
Problem
- Code is duplicated between microservices leading to a low code reuse. That is especially hard to govern and new versions might not be propagated to all microservices.
- Shared libraries including data models (e.g. for the API) or even other service internals are hampering independent deployments by break
Solution
Don't share domain specific code as libraries, but only general-purpose code for cross-cutting functionality.
Sharing domain specific code (as data models or business logic) increases the coupling between microservices. Changes would have to be adopted by all consuming microservices at once instead of ensuring their independence by letting them upgrade gradually. If domain-specific business logic needs to be shared, consider providing the logic as a service via an API in a new or an existing microservice instead of using shared libraries.
Examples for sharable general-purpose code for cross-cutting functionality are
- formatting log messages,
- providing default monitoring metrics, or
- connecting to infrastructure components.
They have in common that they usually don't change as frequently as domain-specific code.
At otto.de, they even open source those general purpose libraries L14. They report to experience higher quality standards if code is made publicly available. This increased quality plays well into creating shared libraries as they affect all microservices.
Maturity
Proposed, requires evaluation.
Sources of Evidence
L14:
- Code sharing at otto.de
- cross-functional requirements
- regular data import jobs since to ployglot persistence
- extract to libraries
- (-) tight coupling of service implementations
- (-) restrict teams in freedom to choose best solution to their problem
- don't share code between services
- exception: framework code as open source software and frontend assets
- general purpose code, loosely coupled, highly coherent, "good" enough to be open source
- open source aspect: devs seem to apply higher quality standards if they know that code will be publicly available
- (+) keep teams and applications as independent and loosely coupled as possible
- no force to use open source libs => can write their own solutions
- cross-functional requirements
L22:
- James Lewis: shared nothing approach applied to domain knowledge
- always program against external interfaces than reusing domain library
- Mike: when migrating
- decouple shared interface from internal models and source code
L30:
- sharing code between services and shared db is discouraged
- may lead to tight coupling
- loss of autonomy
L31:
- Migration best practice 5: Change code dependency to service call
- Problem: when appropriate to change code-level dependency to service-level dependency and when not?
- Solution
- try keep services' code as separate as possible
- otherwise possibility to break build of other services when changing shared code
- code that rarely changes is okay to be shared
- e.g. string manipulation library
- sharing internal entities or interface schema should be prohibited
- would force dependent service to change immediately even though they want to change gradually
- Share functionality as service instead => new service / attached to an existing one
- Different scalability needs needs between shared library and the dependent services also good reason to changing shared lib to a service => should be new service
- try keep services' code as separate as possible
- Challenges
- Overhead by network call => performance
- caching can handle, but complexity
- Overhead by network call => performance
L34:
- Price for flexibility of microservices: redefine data definitions and business rules across services
- replication in databases
- lacking centralized view on overall system processing, rules, constraints, etc.
- Open challenges: Cross-cutting issues with code replication across services
L41:
- Example system
- Monolith had many shared components
- Microservices reduced to only one: the Lambda framework
- minimal framework to connect to infrastructure and provide standard formatting methods
- messages, logs, health checks
- minimal framework to connect to infrastructure and provide standard formatting methods
L46:
- Autonomy of services
- not sharing code or infrastructure
LM45:
- Context: interviews and insights from multiple cases on technologies and sw quality in MSA
- P7 warned against sharing libraries between services
LM48:
- Context: microservice migration describes an examples project (FX Core) and compares back to monolith
- Independent deployment => no dependency hell
- only shared component is Lambda but still no directly shared dependencies like libraries
- even though if Lambda updated, does not affect other services due to containerization
Interview E:
- Context: components between domains
- not always possible to resolve this ambiguity
- dependent on application domain and mindest of the people
- separate ways often easier than thought
- we grew up where we learned DRY => need to leave that way of thinking behind
- still, there are areas with global components
- example: UI component for complexgrid system, important same view in all subdomains => share that component
- Context: UI widgets and redundancy
- Question if it really is an rdundancy => often different views in different domains on the same thing
- they have the same name but are a different view on it
- For simple things: rather introduce redundancies
- For complex things with same view on it in all domains: then share the component