More on this book
Community
Kindle Notes & Highlights
by
Sam Newman
Read between
December 20, 2018 - January 14, 2019
Domain-driven design. Continuous delivery. On-demand virtualization. Infrastructure automation. Small autonomous teams. Systems at scale. Microservices have emerged from this world. They weren’t invented or described before the fact; they emerged as a trend, or a pattern, from real-world use. But
Small, and Focused on Doing One Thing Well
Codebases grow as we write code to add new features. Over time, it can be difficult to know where a change needs to be made because the codebase is so large. Despite
Microservices take this same approach to independent services. We focus our service boundaries on business boundaries, making it obvious where code lives for a given piece of functionality.
The question I am often asked is how small is small? Giving
a number for lines of code is problematic, as some languages are more expressive than others and can therefore do more in fewer lines of code.
A strong factor in helping us answer how small? is how well the service aligns to team
structures. If the codebase is too big to be managed by a small team, looking to break it down is very sensible. We’ll talk more about organizational alignment later on.
Autonomous
Our microservice is a separate entity. It might be deployed as an isolated service on a platform as a service (PAAS), or it might be its own operating system process.
All communication between the services themselves are via network calls, to enforce separation between the services and avoid the perils of tight coupling.
These services need to be able to change independently of each other, and be deployed by themselves without requiring consumers to change.
Our service exposes an application programming interface (API), and collaborating services communicate
with us via those APIs. We also need to think about what technology is appropriate to ensure that this itself doesn’t couple consumers.
Technology Heterogeneity
With a system composed of multiple, collaborating services, we can decide to use different technologies inside each one. This
A key concept in resilience engineering is the bulkhead. If one component of a system fails, but that failure doesn’t cascade, you can isolate the problem and the rest of the system can carry on working.
With a large, monolithic service, we have to scale everything together. One small part of our overall system is constrained in performance, but if that behavior is locked up in a giant monolithic application, we have to handle
A one-line change to a million-line-long monolithic application requires the whole application to be deployed in order to release the change.
Organizational Alignment
Many of us have experienced the problems associated with large teams and large codebases. These problems can be exacerbated when the team is distributed. We
Microservices allow us to better align our architecture to our organization, helping us minimize the number of people working on any one codebase to hit the sweet spot of team size and productivity.
One of the key promises of distributed systems and service-oriented architectures is that we open up opportunities for reuse of functionality. With microservices, we allow for our functionality to be consumed in different ways
With our individual services being small in size, the cost to replace them with a better implementation, or even delete them altogether, is much easier to manage. How
SOA at its heart is a very sensible idea. However, despite many efforts, there is a lack of good consensus on how to do SOA well. In my opinion, much of the industry has failed to look holistically enough at the problem and present a compelling alternative to the narrative set out by various vendors in this space.
A very standard decompositional technique that is built into virtually any language is breaking down a codebase into multiple libraries. These libraries may be provided by third parties, or created in your own organization.
The Open Source Gateway Initiative (OSGI) is worth calling out as one technology-specific approach to modular decomposition. Java
Our requirements shift more rapidly than they do for people who design and build buildings — as do the tools and techniques at our disposal. The things we create are not fixed points in time.
So, to continue the metaphor of the architect as town planner for a moment, what are our zones? These are our service boundaries, or perhaps coarse-grained groups of services. As architects, we need to worry much less about what happens inside the zone than what happens between the
That means we need to spend time thinking about how our services talk to each other, or ensuring that we can properly monitor the overall health of our system. How involved we get inside the zone will vary somewhat.
be Graphite, and for your health it might be Nagios.
two open source, JVM-based microcontainers. They
am a strong believer that great software comes from great people. If you worry only about the technology side of the equation, you’re missing way more than half of the picture.
In many ways, having an existing codebase you want to decompose into microservices is much easier than trying to go to microservices from the beginning.
The same terms and ideas that are shared between parts of your organization should be reflected in your interfaces.
Making decisions to model service boundaries along technical seams isn’t always wrong. I have certainly seen this make lots of sense when an organization is looking to achieve certain performance objectives, for example.
secondary driver for finding these seams, not your primary one.
I recommend Vaughn Vernon’s book Implementing Domain-Driven Design (Addison-Wesley) to help you understand
I strongly recommend you take a look at the Richardson Maturity Model, where the different styles of REST are compared.
Another principle introduced in REST that can help us avoid the coupling between client and server is the concept of hypermedia as the engine of application state (often abbreviated as HATEOAS, and boy, did it need an abbreviation). This is fairly dense wording and a fairly interesting concept, so let’s break it down a bit.
For example, some frameworks actually make it very easy to simply take database representations of objects, deserialize them into in-process objects, and then directly expose these externally. I
Shared code between client and server can be very dangerous, as we’ll discuss in “DRY and the Perils of Code Reuse in a Microservice World”.
Despite these disadvantages, REST over HTTP is a sensible default choice for service-to-service interactions. If you want to know more, I recommend REST in Practice (O’Reilly), which covers the topic of REST over HTTP in depth.
However, vendors tend to want to package lots of software with them, which can lead to more and more smarts being
pushed into the middleware, as evidenced by things like the Enterprise Service Bus. Make sure you know what you’re getting: keep your middleware dumb, and keep the smarts in the endpoints.
also strongly recommend Enterprise Integration Patterns (Addison-Wesley), which contains a lot more detail on the different programming patterns that you may need
As you find yourself making more service calls, especially when making multiple calls to perform a single operation, take a look at the reactive extensions for your chosen technology stack. You may be surprised how much simpler your life can become.
code that can be reused. We pull duplicated code into abstractions that we can then call from multiple places. Perhaps we go as far as making a shared library that we can use everywhere! This approach, however, can be deceptively dangerous in a microservice architecture.
If your use of shared code ever leaks outside your service boundary, you have introduced a potential form of coupling.
I’ve spoken to more than one team who has insisted that creating client libraries for your services is an essential part of creating services in the first place. The