-
Notifications
You must be signed in to change notification settings - Fork 9
Microservices
- Services
- Architecture of Microservices
- Implementing Microservices
- Deploying Microservices
- The Twelve Factors
- References
A service is the technical authority for a specific business capability (what).
- Business processes (how), company organization (who) or technologies are not service boundaries.
- Business processes tend to contain multiple business capabilities.
- The that provides any meaning at all to the concept of a "service" in software terms is that it represents a boundary. [c]
- Any piece of data or rule must be owned by only one service.
- The concerns of a single cohesive business capability should be supported by a single service. Not by many process-, task- and entity-services.
- Entities (like Customer, Order, and Product) are almost always the wrong place to start for identifying services.
Services are coarse grained units of logic.
- A service that has only functionality is a function not a service (like calculation, validation).
- A service that has only data is a database not a service (like CRUD entity).
- A service is autonomous, its ability to function is not controlled or inhibited by other services.
- services can decide on their own.
- self-governing, self-controlling, independent, self-contained, and free from external control and constraint.
- you first need team autonomy on an organizational and functional level that then is supported by microservices on a technical level.
Services interact by way of exchanging explicitly defined messages via their endpoints. The messages and endpoints are described by service contracts.
- A service interacts with other services using asynchronous communication patterns.
- SOA models stable business abstractions. That's why it doesn't fit when business is not well known - like in startups or generic extensible "platforms".
- With event-driven collaboration, we want to broadcast an event, and a typical way to implement that broadcast would be to put the event into a message. The message is the medium; the event is the payload. [a]
The nature of interactions between services should be such that no service should rely on the immediate availability of another service in order to handle a message. That is, we want to eliminate temporal coupling between services by limiting ourselves to asynchronous communication between services.
The more autonomous our services are, the more loosely coupled they are. And loose coupling is what gives us business agility, which is the key business driver for SOA.
Functions are an abstract, customer-facing definition. The only thing they call upon are processes. Processes combine services in a specific sequence to deliver on a function. Services combine capabilities, usually executed according to their own internal process. Capabilities are the granular, individual tasks that, collectively, make things happen.
Application is a piece of software leveraged by a user (via a user interface) to perform a task of value to the user.
- A service never directly aligns with an application.
- A service may contain zero or more applications - zero if the service is fully automated.
- An application may span many services (in the case of a composite application).
Business components are alternatives to regular business services for specific use cases.
- regular customers vs. strategic customers
- regular shipping vs shipping of frozen goods / meat
- basically forks of services
- max two: strict boundaries which don't change
- a customer is either regular or strategic - will stay for lifetime
- no gold, silver, customers - this can change
Autonomous components are deployment units of business components (Maven artifacts etc.)
- AC for API, AC for backend, AC for frontend etc.
- not necessarily physically autonomous
ACs, BCs and Services
- Service is the logical view, one code repository,
- BCs are siblings, different manifestations of the same service, no interactions with each other,
- ACs are the packaging assembling view, how to put everything into system
+------------------+
| BC [AC][AC][AC] |
Service |------------------|
| BC [AC][AC][AC] |
+------------------+
Building a distributed solution is not always more complex than building a monolithic one. The primary difference is that in distributed systems we tend to model the necessary complexity in the domain and make it explicit through the process to minimize the accidental complexity. In a monolithic system, because there is usually no explicit modeling of necessary complexity it is more difficult to know when we cross the boundary from necessary to accidental complexity - that makes it harder to understand (because the complexity is internal and not modeled).
To design a proper microservices-based system, we have to optimize both global (between services) and local (within a service) complexities. [b]
A physically monolithic deployment is okay for most cases, a logically monolithic design, where everything is coupled to everything, is evil.
Don't distribute your objects - states that no remote calls outside the service should be made in order to get all data needed to fulfill the particular use case.
A monolith is complicated, distributed systems are inherently complex.
In the context of SOA, each service will have its own Domain Model. We strive for high cohesion within a service domain model by ensuring that the described business area is highly specific but loosely coupled with business areas supported by other services.
The problem with modeling an entity a whole in a single place is that different services understand it differently and typically need only a part of it. Therefore, it's better to model only fragments of what and where is needed.
By modeling entities in code, try to ignore their physical representation. Customer objects, for example, doesn't have to contain all fields from a real customer.
Bounded contexts, which are so important to DDD, are explicitly about hiding information - presenting a clear boundary to the wider system while hiding internal complexity that is able to change without impacting other parts of the system. This means that when we follow a DDD approach, whether we realize it or not, we are also adopting information hiding - and as we've seen, this is vital in helping to find stable microservices boundaries. [a]
Microservices is an organizational-scaling pattern. That is its advantage. If you don’t need to scale up development in your organization, you don’t need microservices (although “services” may be a great idea)! [c]
-
Think of microservices as a specific approach for SOA.
-
The architectural decisions here shouldn’t be made by the individual developer who is building a microservice, but should be part of the architectural design of the microservice ecosystem as a whole.
-
Model around business concepts.
-
Consider starting monolithic first and break things out when they are stable.
-
Adopt a culture of automation.
-
Hide internal implementation details.
-
Decentralize all the things.
-
A microservice for an aggregate (DDD) is a perfect match.
-
Small teams working on small codebases tend to be more productive (two-pizza teams).
-
Full stack teams rather than full stack developers. [a]
- Moving specialist out of dedicated teams won't inhibit the specialists' ability to do their job; in fact, it will likely increase the bandwidth they have to focus on the difficult problems that really need their attention. [a]
Experience has shown that changes in functionality typically span multiple layers in these types of architectures - requiring changes in presentation, application, and data tiers. [a]
With microservices we have made a decision to prioritize high cohesion of business functionality over high cohesion of technical functionality. [a]
- The physical servers (owned by the company or rented from cloud providers)
- Databases (dedicated and/or shared)
- The operating system
- Resource isolation and abstraction
- Configuration management
- Logging and monitoring
- Network
- DNS
- Endpoints
- Messaging
- Service registry & Service discovery
- Load balancing
- Self-service internal development tools
- Development environment
- Test, package, build, and release tools
- Deployment pipeline
- Microservice-level logging and monitoring
- See monitoring as an activity - something we can do - with observability being a property of the system. [a]
- The microservices
- All microservice-specific configurations
Avoid introducing pure infrastructure teams. Distributing responsibility for shared services to the product teams can be a good alternative model.
-
Shared code can create very coupling.
-
Shared libraries are okay for common tasks that are not specific to the business domain.
-
Don't violate DRY within a microservice, but be relaxed about violating DRY across all services.
-
Microservices have all the associated complexities of distributed systems.
-
There is no one “best” language to write a microservice in, but there are languages that are better suited than others to certain types of microservices.
-
Keep the middleware (as a message queue) dumb, and keep the smarts in the endpoints.
- The "smarts" in our system want to live in our code, where we can have full control over them. The API gateway, for example, is a pipe - we want it as simple as possible. With microservices, we are pushing for a model in which changes can be made and more easily released through independent deployability. Keeping smarts in our microservices helps this. If we now also have to make changes in intermediate layers, things become more problematic. [a]
-
Shared database is a bad idea: allows external parties to view and bind to internal implementation details, and ties consumers to a specific technology choice - we lose both strong cohesion and loose coupling.
-
Event-based collaboration is highly decoupled.
-
With SOA the unit of reuse should be the event, not the function.
-
Asynchronous programming style is more decoupled and scalable, but increases in complexity.
-
Try to avoid overusing the API Gateway (anti)pattern. Don't put too much logic in it.
-
Implementation of the pattern backends for frontends should stay in the services themselves.
-
Prefer choreography over orchestration.
With a service mesh, common functionality associated with inter-microservice communication is pushed into the mesh. This reduces the functionality that a microservice needs to implement internally, while also providing consistency across how certain things are done. [a]
Saga is a long-running (long life-cycle) process with a state (encapsulates the process state).
- Triggered by messages.
Saga has its own state, data.
- Doesn't modify or query master data, only its private state (self-contained).
Saga handles typically multiple types of messages (a message-handler only one).
- Send messages to communicate with services.
- Should be unit-tested (in opposite to message-handlers).
Saga belongs always to only one business capability.
The entire sage should be owned by a single team.
The trickiest idea here is that the services are “independently deployable.” Independently deployable components of software have been around for a long time in lots of different contexts, but now they are part of the definition of an architectural style and a central part. This is the key defining characteristic of microservices without this idea; they don’t introduce anything new. [c]
The real value in microservices is that we can build, test, and deploy them independently, of other services that they run alongside, and even of other services that they interact with. [c]
Microservices are an organization de-coupling strategy.
- A microservice can be deployed independently of the rest of the system.
- Prefer self-contained deployable microservices as artifacts.
- Treat staging and production as separate “deployments” or “phases” of the same microservice.
- Avoid versioning microservices and endpoints (Microservices should be treated as living, changing things, not static releases or libraries).
- Run health checks on a separate channel (“200 OK” on a
/health
endpoint). - Avoid single points of failure.
- Avoid adding debugging logs in code that will be deployed to production — such logs are very costly.
- Alerts must be set up for all key metrics.
Ensure that you embrace the concept of independent deployability of your microservices. Get into the habit of deploying and releasing changes to a single microservice into production without having to deploy anything else. [a]
Full staging - No communication between production and staging.
- Coordinate and/or schedule deployments to staging to avoid the deployment of one service breaking the staging environment for all other related services. Partial staging - Microservices have their own staging environment, they communicate with the upstream clients and downstream dependencies that are running in production.
- Production services can easily be taken down by bad staging deploys.
Resiliency testing: code testing, load testing, chaos testing.
- Chaos testing is best provided as a service, and not implemented in various ad hoc manners across development teams.
- Chaos engineering is a technique for finding failures before they happen.
-
I. Codebase
- One codebase tracked in revision control, many deploys.
- No need to recompile for different environments.
-
II. Dependencies
- Explicit declare and isolate dependencies.
- No implicit system-wide packages.
-
III. Config
- Store config in the environment.
- Anything that differs in dev/prod etc.
- Not in properties files etc. - it's a code.
- Use properties (or YAML etc.) only as a hierarchical abstraction.
-
IV. Backing Services (DB, REST, SMTP, ...)
- Treat backing services as attached resources.
- Thru URLs, never locally.
- No code change is needed.
-
V. (Design), Build, Release, Run
- Strictly separate build and run stages.
-
VI. Processes
- Execute the app as one or more stateless processes.
- Design for failure.
- Shared-nothing architecture.
-
VII. Port Binding
- Export services via port binding.
- Use a port per service.
- Mapping is done by the platform.
-
VIII. Concurrency
- Scale out via the process model.
- Design ability to horizontal scaling.
-
IX. Disposability
- Maximize robustness with fast startup and graceful shutdown.
- As we increase the robustness of our application, we introduce more complexity to our system. [a]
- Maximize robustness with fast startup and graceful shutdown.
-
X. Dev/Prod Parity
- Keep development, staging, and production as similar as possible.
- Designed for Continuous Delivery.
-
XI. Logs
- Treat logs as event stream.
- Log to stdout or stderr - the platform implements then the handling.
-
XII. Admin Processes
- Run admin/management tasks as one-off processes.
- Like Kubernetes side-cars.
-
XIII. Audit
-
XIV. AuthN/AuthZ, Security
-
XV. API First
- Microservices by Martin Fowler
- Don't distribute your objects
- Sam Newman: Building Microservices [a]
- Vlad Khononov: Learning Domain-Driven Design [b]
- Susan J. Fowler: Production‑Ready Microservices
- David Farley: Modern Software Engineering [c]
- Sagas: https://www.youtube.com/watch?v=H-kVOxEwQzk
- Complex Event Flows in Distributed Systems
- Service Mesh: https://servicemesh.io
- Michael Nygard: Grinding the Monolith
- Why microservices fail
- How to define service boundaries link2
- Business-Capability mapping
- The Known Unknowns of SOA
- Layered service models are bad
- Fallacy of service reuse
- Anatomy of service: Part I Part II
- Domain Modeling in SOA
- Why should be services autonomous
- Service granularity
- CRUD services are bad
- Avoid command messages
- Microservice Architecture at Medium.com
- Micro Frontends in Action
- Untangling microservices
- What are Aggregates in DDD
- Bounded Contexts are NOT Microservices
- Finding your service boundaries
- All our aggregates are wrong
- When to use Microservices
- Goodbye, microservices!
- Deconstructing the CAP theorem
- Microservice Design IDEALS
- Right vs. right now
- Exactly one - Outbox pattern