Transcript
Lucas Dohmen: Hello and welcome to a new conversation about software engineering. My name is Lucas Dohmen and today on the CaSE Podcast we will talk about microservices. My guest is Eberhard Wolff.
Lucas Dohmen: Eberhard is a colleague of mine working as a fellow at innoQ. With over 15 years of experience as an architect and consultant, he's written multiple books. Welcome to the show, Eberhard!
Eberhard Wolff: Thanks for having me.
Lucas Dohmen: Your wrote multiple books about microservices, right?
Eberhard Wolff: Yes. I wrote a book about microservices originally in German; it has been translated to English and Korean. I also wrote a rather short book that you can actually get for free, called The Microservices Primer, that gives a very short introduction about what microservices are and why you might want to use them.
Lucas Dohmen: And the primer is in English, as well?
Eberhard Wolff: There is an English and a German version for that. We will put the links to those in the show notes.
Lucas Dohmen: Yes, we will do that. Let's start with a short definition of microservices. What is in your opinion the definition of microservices?
Eberhard Wolff: That's already a very good question, because if you look at it, everybody has a different concept about what a microservice actually is. I tried to come up with a definition that is rather generic, that basically says everything we call microservice actually fits the definition. The definition that I came up with is that a microservice is an individually deployable module.
Eberhard Wolff: The reason why I like the definition is because first of all it basically says that microservices are just modules, and that's a concept that we are very used to. It also makes clear that you can either use microservices or different types of modularization, like Java packages or C++ namespaces - all these kinds of modules that we all used in programming languages.
Eberhard Wolff: It also means that we can apply the ideas about what good modules are also for microservices - high cohesion and low coupling etc. The really new stuff is that they are individually deployable, so if you ever change to a module, you don't need to deploy all of your modules, you just deploy a single module.
Eberhard Wolff: In an e-commerce application if there is a change to the order process, you just deploy the microservice that is responsible through the order process, and the rest of the system just stays as it is. That's the way that I think about microservices and how I would define that term.
Lucas Dohmen: So for you the core ideas are about deployment for microservices, right?
Eberhard Wolff: Yes, that's what sets them apart from other types of modularization. However, it's still modularization. It's still about "How do you split a system apart into specific parts?"
Lucas Dohmen: What do you think enabled all those ideas? Because there are a lot of different ideas about how to exactly do microservices, but they are all not that old, right?
Eberhard Wolff: Yes, that's true. If you look at it, the idea of modularization is very old, but the idea to have individually-deployable modules is actually more recent. I would argue that one of the reasons why we are doing that nowadays is because the cost for deployment has come down a lot. That is because we have virtualization, that is because we have containers, so it's much easier to have a system to deploy software on. Nobody needs to go into a data center anymore and put another system into the rack. It is something that you just do in software.
Eberhard Wolff: Also, the deployment has been automated, due to the continuous delivery movement. I would argue - and that's also how I approach the subject... So I wrote a book about continuous delivery. I thought that continuous delivery was really a huge step forward, and then I realized that continuous delivery is also something that would influence the architecture and that is why I became interested in microservices. I think there is a huge synergy between those two things.
Lucas Dohmen: You are saying that continuous delivery enabled microservices, or it made it a consequence of continuous delivery?
Eberhard Wolff: Yes. In a way it enabled it, but it's also that if you want to do microservices, you need to have automated deployment in place. Microservices also support continuous delivery.
Eberhard Wolff: If you look at it, I would argue if you have a very large system that needs to be deployed as a whole, a deployment monolith, it will be hard to truly do continuous delivery because it will be very hard to even deploy it automatically, and also the fast feedback that you aim for in continuous delivery will be rather slow, because the unit is so large that you're deploying... It takes a long time to deploy it, it takes a long time to test it, and so on.
Eberhard Wolff: I would argue on the one hand, you need to have that automation in place that continuous delivery provides, but at the same time if you want to do continuous delivery, you should think about what you do with your architecture and how your architecture will support continuous delivery. Microservices are a good answer to that topic, to that challenge.
Lucas Dohmen: What other advantages do you see in this architecture?
Eberhard Wolff: That's a very good question. If you look at it, a lot of people claim that the main problem that microservices solve is that large systems are rather hard to develop with large teams. The usual story about microservices is in fact that people had a deployment monolith, and they were throwing more developers on the deployment monolith to get more stuff done, and they realized that the deployment monolith is just too large and it's too hard to really do a lot with it. What they did is they split it apart and built microservices instead.
Eberhard Wolff: I think that's a very valid point, and I would argue that it allows us to tackle problems that were really hard to tackle before. Doing large projects has been a very tough challenge in the past, and in a way, microservices solved that problem because they split apart the system so much that you're rather doing a lot of small projects instead of one large project.
Eberhard Wolff: All the decisions that you're making are usually limited to your microservice. If you have a new feature, it will be implemented in one microservice, and even technical decisions can be made within just one microservice. So there is a lot of independence, and that allows the teams to work independently, and that solves that problem.
Eberhard Wolff: Having said that, a lot of customers that I work with actually have much smaller teams. They have one or two Scrum teams, and obviously in such environments you don't really have the challenges that you have if you have 100 people working on a deployment monolith. I would still argue that there are a lot of advantages that you can gain in such scenarios.
Eberhard Wolff: We already spoke about continuous delivery, so that's obviously an advantage that you can get. There is a very interesting thing - one thing that we're also struggling with as an industry is "How do we actually make sure that our system stays maintainable in the long run?"
Eberhard Wolff: The usual approach is to come up with a very great architecture and to make sure that that architecture will tackle the problem and solve it once and for all. The problem that I have is usually when I go to a customer, they don't have that; even though they tried, they don't have a clear architecture. Usually, they have rather a mess, so I would argue the idea to make a system maintainable in the long run by building a very good architecture - it's something that a lot of people tried, and most of them failed.
Eberhard Wolff: Microservices have a different approach, because microservices basically say "Well, if we mess up one microservice, we can just replace it." That's a nice, different approach. It doesn't say we are going to build that software and it's going to last forever, but rather we say "If there's a problem with one piece of that software, we are going to replace it." I think that's a nice alternative approach.
Eberhard Wolff: The architecture between the microservices is very stable also because there are the interfaces between the microservices, and that means that if you violate the architecture, if you build in a new dependency that shouldn't be there between microservices, you will notice that because you need to go through the interface of the microservice.
Eberhard Wolff: In a deployment monolith, usually these dependencies between modules are just randomly introduced by some developer, and maybe that developer doesn't really even notice how he/she is changing the architecture of the system and how new dependencies are introduced. That is something that in the microservices system you will notice for sure.
Lucas Dohmen: So basically building stronger walls between the modules, right? It's not that simple to get over those walls, because it's very visible.
Eberhard Wolff: Exactly. The term that I'm using for that sometimes is "architecture firewalls" - sort of like splitting the system and making sure that at least that kind of decomposition stays stable in the long run.
Eberhard Wolff: Then there are some technical advantages. For example, you can scale each microservice individually, you can limit or contain a security breach just for a microservice, because there might be firewalls between them, and they run on different systems.
Eberhard Wolff: I would argue that there are quite a few different advantages on very different levels - technical levels, organizational levels, architectural levels, and that is also why a very important point is to get to understand what type of goals you are trying to achieve by using microservices. Depending on what your goals are, you might end up with a very different architecture and a different way of building your system.
Eberhard Wolff: If you have a large system and it's about scaling your process and scaling your development in terms of developers working on it, it's probably going to be different what you're doing from a scenario where you are aiming for independent scalability, for example.
Lucas Dohmen: Do you also see disadvantages in this architecture?
Eberhard Wolff: Yes, for sure. I strongly believe that every type of architecture that you're using is actually a tradeoff, and for that reason there are clearly disadvantages in microservices. I would argue that the number one disadvantage is that the system becomes more complex to deploy and to operate. Basically, what I'm saying is each module becomes an individually deployable thing that is running somewhere - it might be a Docker container, for example. That means that there is just so much more stuff running there.
Eberhard Wolff: If you have a system that contains of 15 modules, there are 15 times the number of things that you need to run in production. That is clearly one of the disadvantages.
Eberhard Wolff: There are some other problems. It is somewhat harder to talk about the system as a whole. If you want to do a refactoring, but you really want to change the whole system, the complete architecture, it's going to be harder. I have to admit that I think that is not such a big problem, because if you want to do that kind of refactoring, it's probably going to be hard anyway, and there is a major problem probably, and it's going to cost a lot of effort anyway.
Eberhard Wolff: This kind of refactoring will be hard anyway. It is harder with microservices, but that's probably something that is still fine. However, having said that, in a way it means that if you get the architecture wrong, if you get the split into modules wrong, it's probably going to be worse if you do microservices... Because if you have a deployment monolith, you can still change all the modules and deploy them at once, while with microservices you would need to do multiple deployments and you need to coordinate those deployments and it's going to be harder.
Eberhard Wolff: If you have a messed up architecture, it's probably going to be worse if you implement that architecture using microservice. That's probably one of the main problems that you have.
Lucas Dohmen: Also, you have a distributed system now, right?
Eberhard Wolff: Yes, you do. However, I would actually argue that the impact of that is rather limited. What I keep saying is that a request that comes in should ideally be handled by just one microservice. Then of course you do have a distributed system, because there are microservices, so each request might be handled by a different microservice, but it should not be the common case that a request will be handled by multiple systems and that there are these kinds of dependencies.
Eberhard Wolff: In a way, what I'm just saying is that microservices are modules, so they should have low coupling; they shouldn't really talk that much to one another. I'm just rephrasing that, I'm just saying that they are a distributed system, but they still don't talk so much to one another because there's low coupling. It sort of makes sense from that perspective, too.
Lucas Dohmen: We will talk a little bit about communication later, but first let's go into preconditions. What do you think a company has to fulfill before they can even start a microservice architecture? Or can anybody just start with a microservice architecture?
Eberhard Wolff: I would argue that it's a global tradeoff between making development probably easier and making operations somewhat harder. For that reason, you need to have some kind of operations environment that actually supports that. In a way, that means you would have to have some DevOps culture, so you need to have some collaboration between development and operations, because if you just do it without a DevOps culture, operations will have a hard time keeping up with all the microservices, making sure that all of them work and are actually available. Development needs to support them.
Eberhard Wolff: At the same time, operations needs to realize that they are up to a challenge, but it's worthwhile because it makes life easier for the dev department. My experience is that you don't need DevOps teams in the sense that one team is responsible for building the service and running it, so the "you build it, you run it" approach - that's not strictly needed; it is rather that the operations department needs to support the dev department and vice-versa. There needs to be some collaboration. It's not that you need to have these DevOps teams that are fully responsible for the services. That's just my experience, so in practice it is still fine to have separated teams between development and operations. That's the main point I would argue that you need to support.
Lucas Dohmen: I heard that there are a lot of different styles of microservices. You said that there's no definition of microservices, but there are a few out there. Can you mention some of them, maybe the most important ones?
Eberhard Wolff: I would argue that the most radical approach that I know concerning microservices is what Fred George implements. There is an interview that I did with him on the SE Radio Podcast where we go into a lot more detail. He says that a microservice is something that is like a hundred or maybe two hundred lines of code, and that can be pretty much replaced instantly. That's an approach where you have really small-grained microservices, and communication between them is asynchronous.
Eberhard Wolff: It is not an approach that I would propose if I was running a project, because frankly I'm not sure how you can actually make that happen because it is a lot of very small things that are running, and that means it's going to be very complex to actually support that. However, Fred has done that in a lot of different projects, so it is something that definitely works in practice.
Eberhard Wolff: That's the most radical approach. It is something that you would probably nowadays would be able to do using serverless systems where you can have functions of a hundred lines of code or so, just type the code in the browser and run it on the Amazon Cloud and off you go. That allows you to do more fine-grained services.
Eberhard Wolff: The approach that a lot of people seem to be doing is what Netflix and a lot of other companies are doing, where you have microservices that are rather coarse-grained. A Scrum team would handle one or maybe a few microservices, but maybe not a lot of them, so it's more coarse-grained in that sense. The approach that I see working with a lot of customers or that has actually quite some good ideas is the self-contained systems approach.
Eberhard Wolff: It has a similar level of granularity, like the Netflix approach, but it's different because self-contained systems are supposed to be web applications, so they would include a UI and would therefore have an architecture where all the logic, all the data and all the UI is in one service. The big advantage is that if a customer requests a change to the system, it's very likely that it's going to be contained in one self-contained system, even if the customer wants to have a change to the UI and the logic and the persistence; it's going to be in one self-contained system quite likely, so that's an advantage.
Eberhard Wolff: The other advantage is if you do it that way, there is not too much communication between self-contained systems if a request comes in. Let's imagine that you have a system that handles the checkout in an e-commerce system, and a system that is responsible for the invoicing. All the requests concerning checkout, the payment and so on will be handled by one self-contained system, and there is just one communication where the order is handed over from the checkout to the invoicing, and that's it.
Eberhard Wolff: There's a lot of requests that are just handled by one self-contained system, and there is hardly a lot of communication between them. That fulfills the goal of the typical request being handled by just one self-contained system or microservice, for that matter.
Lucas Dohmen: I think we have to mention Conway's law at this point, and maybe briefly describe what this is about, because I think we're talking around it a little bit, right?
Eberhard Wolff: Yes, that's a good point. There is no talk about microservices that is complete without mentioning Conway's law. That's a classic. I believe it's from '68 and it claims that the architecture of a system is limited by the organization's structures.
Eberhard Wolff: The way that you can think about it is that if I'm building a component and someone else is building a component, we would have an interface between them. If there is an architecture, we will probably spread the parts of the architecture, the components to the different people, the different teams. That is how the architecture influences the organization, and it's also the other way around - if I'm part of a team, I will probably build something and someone else will build a different component, and then we will organize our work in that way. Then we will have an interface where we need to talk because of requirements that we might want to implement jointly.
Eberhard Wolff: Communications structure will lead to something in the architecture, and again, if you have something in the architecture, it will lead to people talking to one another. The interesting thing that microservices brought to the table concerning Conway's law is -- before microservices, Conway's law was thought of as a limitation...
Eberhard Wolff: Here's my organization. Let's assume that I built a team. That team is the UI team, and there is another team that is responsible for the database - I do that because there are experts in these areas; there are experts concerning databases, and experts concerning UI. If I do it this way, which makes sense from an organization standpoint, it means that I influenced the architecture and the architecture will have those technical artifacts.
Eberhard Wolff: There is a limitation concerning the architecture that you can actually build, and the new thing that microservices brought to the table is that the microservices people said "Well, we have the ideal architecture, which is more like independent systems. We have some system that has a business functionality such as the checkout, and we assign a team to that system.” That way, we enforce the architecture using organizational measures.
Eberhard Wolff: That is also what the agile community mentions. The agile community also talks about cross-functional teams - that's actually what we are doing now. We need to have teams that have UI experts, experts for domain logic and also experts for databases; we need to have cross-functional teams.
Eberhard Wolff: There is one thing that is also sort of a synergy there... I used to be part of a team (15 years ago or so) where we actually had an order team - they were responsible for order - but we built a deployment monolith. What's the difference to nowadays? Nowadays it's not just that you have the functionality split apart - some new feature will either be handled by the order team or by the checkout team or by some other team, it's also that technical decisions can be made by these teams because they would just influence one of the microservices, and therefore there is more independence.
Eberhard Wolff: There is a synergy there, because microservices allow you to be more independent concerning technology, and it is also easier to do that organizational thing.
Lucas Dohmen: Yes, that sounds reasonable. Where do you see how to implement microservices? Do you think that it is a greenfield approach, or a brownfield approach, or can it be both?
Eberhard Wolff: That's a very good point, and it's valuable to talk about it because microservices are originally a brownfield approach. The usual story that you see in conferences and also that I see in customers is "We have a deployment monolith, we hit the wall concerning the speed that we can do changes, so now we are splitting it apart into microservices." That's actually great, because migrating away from legacy applications is one of the huge challenges that we have as an industry, so microservices are another tool to do that.
Eberhard Wolff: It's also important because it's rather unsexy. Nowadays people think microservices are fancy and cool - and of course they are, but they also solve these old, unfancy, unsexy problems like legacy migrations... So I think that's an important point to notice.
Eberhard Wolff: It seems to me that in the last year or so people are also building greenfield microservices systems. That's something that I see as a rather recent thing, and of course you can do that, but at one point someone stepped up to me and said, "Well, microservices are very cool, but you can only truly do that in a greenfield environment." I was like, "Wow, that's a very strange comment", because to me it is originally about brownfield, and I think it's a very valuable tool in that regard. You can build a new system, you're not tied to the old technology, and you can gain advantages pretty soon without modifying a lot of the code in the legacy system. I think it's a very valuable tool in that scenario.
Lucas Dohmen: One question is how do we handle all this complexity in microservices?
Eberhard Wolff: That's a good point. On the one hand, microservices are of course complex; we already spoke about how deployment and operations become much more complex... I would argue that microservices are actually a simple solution. People shouldn't be using an architecture because it's complex, and I believe they don't do that. They do it because it's the simplest solution around.
Eberhard Wolff: If you look at it, there are a lot of advantages, like these really decoupled systems from a technology perspective and also from a functional perspective (easier scalability etc.). There's a lot of stuff to be gained.
Eberhard Wolff: However, of course, there are some disadvantages, primarily in the space of operations, and that's the tradeoff. I would argue if that tradeoff is positive - if you gain more than you lose - you should do microservices and then chances are that microservices are a simple (or probably even the simplest) solution. That's how the whole concept even was created; people were trying to build systems that they could not possibly build without using microservices, because otherwise they would be too complex. That's a very important point to note - it is not that microservices are complex; it's a trade off, and you should only do it if it's actually a simple solution, not a complex solution. I would never voluntarily choose a complex architecture, because that would backfire on me.
Lucas Dohmen: That sounds reasonable. You said in the beginning that each microservice should be able to handle a request all by itself. How does that work?
Eberhard Wolff: That's just another way of saying that the modules, the microservices have low coupling and high cohesion. Each microservice will hardly talk to another microservice - they are lowly coupled to one another. The question is how do you make sure that your systems are structured in that way? The best tool that we have to do that is domain-driven design. Domain-driven design is a way of structuring software that has been introduced by Eric Evans more than ten years ago; he wrote that book about domain-driven design.
Eberhard Wolff: Among other things, he said that if you have a domain model, it's actually limited to one bounded context, so it's only valid in that one bounded context. In a way, that's trivial. If I have a system in the e-commerce area, I have my domain model in that system, and of course, my customer will have a completely different domain model, as will my suppliers and other systems that I interact with.
Eberhard Wolff: However, even in such a system like an e-commerce system, I can probably identify bounded context; I could say "Okay, if I'm looking to search for products. I'm basically interested in what the product looks like, definitely the price, and a lot of other attributes, like the size of a T-shirt, the color of a T-shirt and all these kinds of things." That's for the product search.
Eberhard Wolff: Then if I try to send out that product to someone, what I'm interested in is in which warehouse is it stored, how much does it weigh, how can I ship it? Is it something that needs to have some kind of special handling, and so on? Those are very different things that you need to know about products, and of course there is a completely different domain model. All of the other entities will be handled differently too, the logic will be different and so on.
Eberhard Wolff: What domain-driven design says is "Let's have domain models for each of these bounded contexts, and that way we can get a much clearer and easier specialized domain model instead of overdoing it and making it too complex." If you have a microservice, that is actually one bounded context, so it implements a bounded context, they will be very independent. So all of the logic, and because the domain model also includes the data, also all of the data will be in that one microservice and it will be handled there, and that's it. They hardly need to talk to one another. Of course, there is some communication because they are still part of a system, but it's not a lot of communication. That is the key, to make sure that microservices don't talk to one another.
Eberhard Wolff: The other interesting thing is -- of course, systems need to talk to one another, and if you look at the more recent ideas in domain-driven design, actually they say that those bounded contexts will probably talk to one another using events. Something like "Okay, here is a new order, and please write an invoice for that, or make sure that the delivery goes out."
Eberhard Wolff: From the domain-driven design perspective, they propose that you should use events, and that can be easily translated to asynchronous communication, which is advantageous for microservices because it allows more resilience, and it solves the problems around the distributed system.
Eberhard Wolff: Distributed systems of course have the problem that systems might be down, but if you have asynchronous communication, it just means that the system will eventually come up and at that point the message will be transferred, and you're fine. Your system will probably deal with a higher latency, but you have to be aware of that latency anyway, because you're using asynchronous communication, and asynchronous communication by definition includes a higher latency.
Eberhard Wolff: There is a nice synergy there again, because domain-driven design gives us more than just bounded contexts, it also sort of enforces asynchronous communication between the bounded contexts, even though it's actually just events, but they can be easily implemented using asynchronous communication and that's quite nice, because it again solves problems that we have in these distributed systems where communication might fail, systems might be down, and you need to take care about the resilience and how the system survives if some parts of the system fail.
Lucas Dohmen: Okay, but when each request is handled by mainly one microservice, how do I get from one request to one microservice to another one? How do they integrate with each other?
Eberhard Wolff: A very simple thing to do there, and that is why the idea around self-contained systems is so powerful - you can just say "I'm going to render an HTML page in one microservice, and then there's a link to another HTML page." If you want to go from your search system to the checkout, it's just going to be a link. That will be one way of doing that. Or if you want to go from the product search to a product detail page, it's just going to be a link, and so on. That's one way of doing it.
Eberhard Wolff: By the way, there are also other nice things that you can do using front-end integration. Maybe there's a feature plan that says "I need to have all the information about a customer on one page." Well, how do you do that? You could store all the information about a customer in one microservice, but that would be a lot of replication of data and a lot of events that need to be handled, so an alternative might be to say "Here's a page, it's going to be composed from content that is provided by the different bounded contexts, and it's all displayed on one page."
Eberhard Wolff: For example, if you have the homepage of an e-commerce system, you might have one part that is filled by a system that is responsible for recommendation, another one that is responsible for giving you an idea about what the state of your last orders are, and so on. These overviews can be handled on the UI level, and the nice thing about it is that it's really low coupling. All these systems need to do is they need to be able to render that page, and that's it.
Eberhard Wolff: Of course, there's some integration... There is CSS, for example, that might be shared, and there is a look and feel that you need to agree on, but if you want to display completely different information, that's fine; you can just do it, and it would just be a change to one Self Contained System. That's one way of doing it.
Eberhard Wolff: The asynchronous communication and the events basically mean that in a way there is kind of a data replication, even though I'm not sure whether that's the correct term that I would use. What I'm trying to say is there are events, for example "Here is a new order", and of course that will mean that there is information being stored in the invoicing system, and there is information being stored or processed in the system that actually triggers the delivery, but it's going to be very different information. It's not really replication, it's more like "There is an event, and that leads to a system storing data somewhere, and doing that kind of stuff."
Eberhard Wolff: Of course, there is still the possibility to do synchronous integration. If you look at the Netflix architecture, they rely a lot on synchronous communication. But in that case, you need to deal with the problem that a system might be down and you need to build in some resilience in one way or another, so you need to deal with that. That's why I would rather stay away from it.
Eberhard Wolff: However, there are still cases where you would need to use synchronous communication, and that is when you want to make sure that there is a consistent view on the data. The only way that you can get a high level of consistency is by having one system that has the data and all of the systems doing synchronous call to get the most up-to-date information about the data, because that way you eliminate the event mechanism, the asynchronous communication and the lag time that you might have between the different systems processing the events.
Eberhard Wolff: Apart from that, I would rather stay away from synchronous communication. It is an option, but I would rather stay away from it. That is clearly a different approach from what Netflix, for example, is doing.
Lucas Dohmen: Okay, but all of that means that the database between those services is not shared, right? They all have their own database.
Eberhard Wolff: Yes, so at least they have a different database schema, and that's just a result of bounded context. If you have your own domain model, you have to somehow store the information in that model and for that reason you need to have your own schema that you can independently change and where you can add some data or remove data. That's quite clear.
Eberhard Wolff: The question is "Does that mean that you have to have a different database for each system?" You might argue that's a good idea, because polyglot persistence basically taught us that there are different databases like graph databases, relational databases and document stores, and they have different advantages and disadvantages, and for that reason you probably want to have a different database in each system. I would argue that's also the case in more traditional architectures.
Eberhard Wolff: If I have some kind of search, it's usually done by some specialized search system. It's not that uncommon to have a specialized persistent solution for a specific bounded context or a specific microservice. Having said that, there is a problem. If a microservice uses a different programming language, or let's say just a different Java framework from an operations perspective, that's not that much of a difference. Whether I have a Java process that runs that jar or that jar doesn't really make a lot of difference. But for databases it's different because those databases actually store data, and that data should not be lost; you need to have some kind of backup, you need to have some kind of disaster recovery, and that means the cost or the effort to have a lot of databases running in your system is quite high.
Eberhard Wolff: For that reason, it's fine if you have one database or just a set of databases that you support, or maybe even just one database instance that all of your microservices use. It's a tradeoff. That would probably be cheaper, but it also has drawbacks. If that database fails, all of your systems fail. That's not a good thing, of course.
Lucas Dohmen: And the deployments are not as independent as they would have been if they had their own databases, right?
Eberhard Wolff: Right. You do introduce some dependencies concerning deployments, you do introduce dependencies concerning availability, and I think that might be a valid trade off; it depends on what you're trying to achieve and what you're trying to do.
Eberhard Wolff: If at one point you figure out that you should rather have separate databases, you can do that probably without a lot of effort.
Lucas Dohmen: If we have multiple services and we now want to do testing, how does that work?
Eberhard Wolff: That's again a good point, and it's often forgotten. As I said, microservices have independent deployment as the main characteristic of them, and to actually achieve independent deployment, you have to have independent tests, too... Because if I have my microservice and I run through my deployment pipeline, and then there is some integration test, and there is a different microservice that is just being integration-tested, I can probably not pass that stage, because first we need to check whether the microservice that is currently being integration-tested is correct before I can enter that stage. Otherwise it's not clear whether my microservice or the other microservice is actually the one that made the integration test fail.
Eberhard Wolff: So there is a bottleneck, and in particular if you have a migration scenario where you have your deployment monolith and you wanna split it apart into microservices. It's very tempting to just take the test that you have for your deployment monolith and declare the integration test for the microservices. Then you do have that bottleneck and it's going to be a huge bottleneck. For that reason, you need to separate those tests, too.
Eberhard Wolff: A problem is "How could that possibly work?" because of course there might be things that must be tested in integration tests... Some functionalities that can only really be tested if you have all of your microservices working together. First of all, let me just say that if you have bounded context, that should not be common, it should be the exception. That is one way of dealing with that problem, or at least realizing that it might not be that bad.
Eberhard Wolff: The other thing is - if you think about it, what's the purpose of an integration test? The purpose of an integration test is to make sure that systems work together, that the integration works. It's not called an acceptance test. It not something where you test whether the software is actually something that the customer would accept... So can we do acceptance tests without integrating all the modules? Well, it turns out you can.
Eberhard Wolff: If I have my system - let's say there's an order system and it talks to an invoicing system; I can check my system with a stub for the invoicing system, and that's something that is rather common. It is something that people now also do. Instead of running the real invoicing system, I just have to stub; that behaves it the way that I intend it to behave.
Eberhard Wolff: Well, there's still a problem, because what about the invoicing system? That one might actually have a problem. It might not behave in the way that I expect it to behave. That is where Consumer-driven contract tests come in. That's also about 10 years old. The idea is basically to write a test. I write a test for invoicing, and I pass it over to invoicing and I say "This is the way that I intend to use your system. I do this call, I expect that result. I do this call, I expect that result" and so on and so forth.
Eberhard Wolff: The nice thing is that now the invoicing system can be tested for correctness (or in an acceptance test) without my system, but instead we would use the Consumer-driven contract testing. That means that now we have two systems that actually talk to one another, but whether they work together correctly can be tested in separate deployment pipelines. I can test whether my system does the right thing by using a stub, and they can use the Consumer-driven contract test that I would provide them to make sure that their system works correctly.
Eberhard Wolff: You might argue "Well, that's all nice, but how can I make sure that the Consumer-driven contract testing and the stub are actually like the real thing?" Of course, that's a challenge, but what you also have to think about is that the risk of an individual deployment is lower, because you do much more frequent deployments because of continuous delivery, and also I just changed one microservice in each deployment, so the risk is lower. That means that you can have alternative approaches and alternative things that you can do. You can roll back to the last deployment if there is a problem in production.
Eberhard Wolff: I would argue that it's not just about doing tests, it's also about the lower risk of deployments that continuous delivery of microservices gives you. That's particularly important if you're looking at performance testing. I would argue that it's very hard to get performance testing right and to actually get a production-like system, all the production data, and simulate the user behavior, particularly if there are new features. Who knows how those features will be used?
Eberhard Wolff: Particularly in that case, I would rather rely on monitoring the ability to do some elastic scaling, the ability to deploy fixes quickly in production and so on, to solve performance problems. With microservices we also have to think more about monitoring getting, deployments quickly out; it's something that would also lower the risk of problems in production, and making it easier to actually solve problems that you might have in production.
Lucas Dohmen: But in a way the CDC would enable us to make the contracts between services more explicit, right? We don't only have those firewalls between the systems, but we also know where the doors between those areas are, right?
Eberhard Wolff: Right, I would fully agree. If you do any kind of interface between teams or developers, you might want to do it using some kind of Consumer-driven contract test, because there are just so many advantages to be gained out of that. It's a clear definition of the interface, it clearly spells out how the interface will be used, and it also means that the interface is more changeable, because if the invoicing system wants to change their interface, they can just do it without talking to anyone, and they can just see whether the Consumer-driven contract test will fail. If it does, they obviously did something that they shouldn't have done, and they can just go back and say "Okay, we are not going to do that" or "We really need to talk to that team."
Eberhard Wolff: If you don't have that, it's much harder to change the interface because you might not even be aware of who uses that interface and how. Then it's more like, "Okay, are we going to do that change? What are we going to break? I don't know, so let's rather not do it."
Lucas Dohmen: Okay, so if we would want to sum up the discussion, what would be your main takeaways for implementing microservices?
Eberhard Wolff: Currently I have somewhat mixed feelings because I think microservices are very fashionable, and there are some (let's say) strange things that I see out in the wild. There are people who want to implement or use microservices but are not sure why they are doing that. If you ask me, it comes down to the question "What are your goals?" Then you can find an architecture, and I think microservices bring a lot to the table.
Eberhard Wolff: As I said, for large systems, they make development much easier; they might even help you to solve organizational issues and communication issues that you might have, and that's great. It's an excellent tool if you want to do continuous delivery. I would not introduce continuous delivery without thinking about the architecture and whether you want to do microservices or not. Also, I think the focus that we are seeing nowadays after almost 15 years concerning domain-driven design - I really like that, because it's such a powerful approach.
Eberhard Wolff: There is a renaissance about a lot of things - domain-driven design, resilience, and these kinds of things. It's great that they are getting into the spotlight again. Having said that, the way that I see microservices now - to me it's another tool and it has a lot of advantages. I would use it as sort of an inspiration about what you can do in your current environment and in your current context to solve the problems at hand. That's the way that I look at it and the way that I would handle it.
Eberhard Wolff: What that means is -- for example, domain-driven design makes a lot of sense, but maybe in your system it's still fine to do a deployment monolith that has a structure that is inspired or that implements domain-driven design. I think that's a very valid decision, and I would prefer that over a decision where you're doing microservices but you can't really spell out why you're doing that and you can't really mention the reasons. If there's a conscious decision about doing your deployment monolith but using some approaches like domain-driven design to structure it, that's excellent. If microservices solve your problem, and I would argue probably they will... Then you should use that approach. It helps a lot with a lot of problems that we are seeing nowadays.
Eberhard Wolff: Of course, we want to get our software out more quickly, of course we have larger teams, of course the things are becoming more complex, and that is where microservices really shine.
Lucas Dohmen: Thank you for talking to me. This was another episode of the CaSE Podcast. Have a nice day!
Eberhard Wolff: Thanks for having me!