“Technical professionals who remain highly focused on loose coupling systems will find this approach can be applied to many design decisions they’ll need to make,” explains Olliffe.
These four steps can help technical professionals to achieve their MSA goals.
Step 1: Define your dependency management strategies
Use distributed configuration to decouple services from the environment: Configuration information can include topic names, queue names, database names and other environmental data. A runtime platform or a stand-alone solution can provide your distributed configuration management.
Use service discovery to decouple services from each other. A service registry application programming interface (API) enables a provider service to register its availability. “The registry can also let consumers discover and connect to service endpoints,” says Olliffe. “This resolution into a service endpoint can be managed on the client side or the service side. Both models keep the link as late-bound as possible to allow the life cycles of consumer and provider instances to be decoupled.”
Step 2: Iterate to identify and extract services
Iterate from the very start. One of the fundamental goals of MSA is to support rapid, fluid iteration of application delivery — this should happen from the beginning.
Use decomposition strategies. Don’t make your transition a “big bang” explosion, and don’t make the minimum viable product for a new application rely on the delivery of tens or hundreds of independent microservices.
Decouple using anticorruption strategies. Where there are dependencies on pre-existing application data or features, you’ll need to design an abstraction layer to ensure the new service remains decoupled. Called an “anticorruption layer” (ACL), its goal is to hide the internal domain models of the legacy environment — and prevent them from corrupting your new service design.
Step 3: Design loosely coupled services
Design your interfaces for loosely coupled consumption — isolate services into self-contained units of deployment.
Keep your service composition loosely coupled. “As your set of services grows, you’ll have to deal with a variety of relationships among them,” says Olliffe. “Some of these services will have simple consumer-provider relationships or publisher-subscriber relationships — but you’re also likely to create a service that’s a composite of existing services at some point.”
Decouple data ownership, persistence, access and reporting. To deliver the greatest level of agility and independence among services, data must be decoupled. There can be no database-enforced relationship (for example, referential integrity) between data owned by one service and data owned by another.
Step 4: Manage service life cycles
Implement a consistent versioning strategy. MSA supports and manages rapid change. You will therefore need a versioning strategy for your services and interfaces:
- Be explicit about how new versions will be identified and deployed.
- Breaking changes to — and hence versioning of — your interfaces should be a last resort.
- For each breaking change, you must support transition periods where the prior version and the latest version of the interface are supported in parallel.
Implement a service dependency tracking mechanism. As the number of microservices grows you’ll undoubtedly ask, “What does my service dependency graph look like?” The dependencies shown could be synchronous or asynchronous, but architects and service developers will still need visibility into which services are using their capabilities and on which capabilities their services rely.
“It is important to understand that fine-grained microservices architecture is typically an implementation detail,” explains Olliffe. Although its design can be complex — and accompanied by its own set of challenges — when implemented properly, the payoff will offer minimized design time, build time and runtime dependencies between services and their consumers.