Goodbye Microservices, Hello Self-Contained Systems - Simon Martinelli
About this talk
This talk explores the transition from microservices to self-contained systems as a more effective architectural approach. The speaker, drawing from decades of experience, discusses his journey from legacy technologies like COBOL at Swiss Railways to modern frameworks such as Spring Boot in contemporary applications. He highlights the misconceptions surrounding microservices, illustrating how many implementations lead to complicated systems often referred to as 'big balls of mud'. By defining concepts like modularization and bounded contexts, he emphasizes the importance of proper system decomposition based on business functionality. He introduces self-contained systems, which encapsulate the user interface, business logic, and data storage within a single module to mitigate communication overhead and improve maintainability, while also addressing real-world customer project experiences.
Full transcript
Hello everybody. I hope you enjoyed lunch and are ready for the next talk. Um, the title of my talk is goodbye microservices, hello self-contained system, but I will not do a microservices bash. I just want to show you a different uh, architecture style. And for that, I like to start with my career. So, 30 years ago, I was that cowboy there doing software in COBOL for Swiss
Railways. And back in the days, we could do more or less what we wanted. So, we did everything. We had what we call today DevOps. Um, but it was an old technology and people were thinking about moving to something new. And I was working for Swiss Railways for a very long time and I had the chance to move to the Java department and started around year 2000
with uh, greenfield Java projects. So, in Europe, Swiss Railways was the second railway company offering online ticketing back then printed on sheets that you had to bring to the train. Now, it's uh, mobile. Uh, but we did there was called J2EE. So, today we talk about Jakarta EE, but uh, around 1999, J2EE came out and a lot of companies working with IBM mainframes uh, moved to Java
because IBM had offerings like uh, IBM WebSphere, for example, application server that you could use to do that. And then, in around the year 2010, a company called Netflix was starting about microservices. And there are certain problems with microservices, and when I look at my customers, for example, I'm working for a large insurance company, the microservices look like that. So, they created so-called big ball of mud
made of mainly because they didn't understand the microservice architecture well. So, I was already introduced, so I can skip that. If you happen to be in Switzerland, I was leading the Java user group there, so you can come and say hello. something that I want to talk about are these two pictures, because there's a misconception about monolithic applications. Because what people often think of when talking about
monolithic applications, they see that picture on the left side. So, that's a big ball of mud. But on the right-hand side, that was also called a monolith. So, that was the Swiss Federal Exhibition, like um 20 years ago or so. And uh this building there was called a monolith made by a French artist called Jean Nouvel. And the interesting thing was, from the outside, it was just
a piece of metal. But in the inside, there were a lot of paintings, so it was very beautiful. That means you shouldn't judge a monolith just from the word monolith. It could either be a big ball of mud, or it could be something beautiful. Today, a lot of people are moving back to monolithic application and call that modular monolith. Or we even have, if you're into Spring
ecosystem, there is a project called Spring Modulith, which helps you to build monolithic applications in a modular way. But that's probably not the best idea, but I will talk about that later. First of all, what we are in fact talking about when we speak about monoliths or microservices or self-contained system, we speak about modularization. And this man here is David Parnas. He was a professor at the
Carnegie Mellon University. in 1972, he wrote a paper about uh the criteria to be used in decomposing systems into modules. And what he said there in this paper is that modularization is only effective if if you're dividing it by the right criteria. Otherwise, you have a lot of coupling maybe between the modules and things will be maybe useless. So, that means as I already said, everything in
architecture is about modularization. We talk about modules and modules are everywhere, right? So, we have modules in terms of maybe microservices. We can terms of packages or we can have modules in terms of components. And all have or share the same definition, so they are and are interchangeable and have an interface. And they can use interfaces from other components. So, I think we could agree on that
definition. but move to the term microservices. You know, naming is hard, but I think microservices is a really bad name for talking about modules. I don't know why Netflix used this term. But the problem with micro is what happens then. Because there is Paul Bakker, I don't know if you know him. He's lead engineer at Netflix. And he's doing a lot of talks about how Netflix uses
microservices. And in one of the talks he says that a at Netflix is usually developed by around seven developers. And if you are seven people, you can fairly build a big microservice, right? So micro is not micro. Micro just means it's a bit smaller. But another thing that happened during introduction is that people were looking back how they could split microservices. And they found out that the
domain-driven design book from Eric Evans has something for them. And this is called bounded contexts. Who of you is using domain-driven design? Who heard about domain-driven design? All right. So the idea of bounded context is you have a domain, for example, I work a lot in wholesale and retail domains. So you have an e-commerce system. And now you like to split that. And splitting that, the best
way to do that is splitting that by business functionality or business boundaries. Like maybe you know the Conway's Law who says the structure of a software always follows the structure of the company. And so, bounded context texts usually are more or less the organization of a company or that could be the starting point to figure out what actually the bound context could be. What's also important, and
that's something that a lot of people will miss when they started to use microservices, we not only have the phase where you start with a new system because probably in at least in days before AI, you had maybe the bigger team that were creating the application than later on during maintenance. And what I also saw in one of my customers is they had so many microservices, they
had problems with updating microservices to the newest technologies, and they realized that they should better put it together again, which could make it more simple for them. Now, if we look at another dimension, so we put the modularity and on one side and on the other side we put the distribution, we can see that if you build something modular, the only difference is the distribution. So, that
means if you have a monolithic application, everything else runs in the same process, and you don't have any remote communication. And on the other hand, if you build microservices, you have But from a high-level point of view, from the architecture design view, that's the same, right? So, if you do modules, it's just a matter of paste, maybe, or more a matter of requirements if you want to
distribute that or not. But if we don't do modular development, then we can end up in this uh big ball of mud. Um and that's okay because you can use tools to analyze that. So, if at least if you use a static typed language like C# or Java, then you can use tools or today you can use AI and re-engineer the structure of the application and try
to uh re-engineer that to make it more modular. But, the problem is if you have a distributed you have the big ball of mud, so that means you have a lot of microservices and they are communicating with each other. Maybe you don't even know how they communicate. This could be a problem. So, one of my customers is doing uh distributed tracing in production to figure out which
microservices in fact call which other microservices. And uh this distributed big ball of mud is something that you can get relatively fast. So, there is Simon Brown. He's an architect from England and he is the creator of the C4 model. And he said, "If you are unable to build a modular monolith, why do you think that microservices are the right solution?" And here is a just a
simple example. Um something that you always see in the textbooks. Uh we have a customer service, an order service, and the catalog. And usually customers and orders are strongly related because you want to know for example, how many orders has a customer or you need to know uh all orders uh that were created by customers from a certain city. And if you split these two you may
end up with something called an N + 1 select problem that usually have a few views or mapping like Hibernate, but this time you have it distributed. So, that means you add even more latency to the problem, and then your application may become way slower. So, you should take care about that. And if you're uh talking about distribution, uh Martin Fowler once said in a book, uh
the first law of distribution is don't distribute. Because distribution adds latency, you have security issues, uh you may have problems with resilience, network is not reliable, the network is not way not always um as fast as possible. So, you have these problems. If you do communication between microservices, you have three choices. You could do synchronous communications. For example, you could uh create some REST services. That's probably
the most common way to do that. Speaking about Netflix, Netflix doesn't use REST anymore. They use GraphQL for the front end and GRPC for microservices com- communication in the meanwhile. But you can also use asynchronous communication for that. Uh because this will help with resilience, that means you don't have to wait for a response. And there are two flavors, so to say. What I call here in
the middle asynchronous means we do request-reply. So, you send the request, and then at some point you get the reply for that. You could even wait for that if you like to. But the question then is what exactly do you do if you wait for it, and you didn't get an answer? And uh one idea could be you just send the request again. But if your consumer
is not idempotent, you probably end up with uh doing things twice. Uh so, a better approach could be we do event-based. Means we do more like something fire and forget. So, we send events, someone consumes that, produces other events that then are consumed in the process at a certain point. And this will result in a much more flexible and more resilient application. But this comes with a
price. First of all, you need kind of a messaging system. You do a traditional like Rabbit MQ or ActiveMQ. Or maybe a more something like an event stream like Apache Kafka. But this has to be maintained. This has to be operated. So, this is not coming for free. When I look at uh microservices architecture with my customers, I often see two anti-patterns. First of all, in my
opinion, is the more problematic is the integration database. So, you have multiple microservices accessing the same schema on the database. So, there you have some issues with responsible for doing the migrations. For example, who is the owner of the data? Uh, who is writing the data? So, that's something that you shouldn't do. But we will see later on I will talk about the project where we migrated
to um self-contained system. And integration database is very often just an intermediate step if you move from a monolithic application to a more modular application. Then as we started to use monolithic uh um microservices in the back end, we as back end developers were fine. By the way, who of you is use is a back end developer? And who is a front end developer? So, we have
way more back-end developers. And for back-end developers, microservices somehow solved the problem, right? So, you had independent deployment, independent scaling. But what companies often did, they just added one UI layer. And this just moves the problem to the front-end because if you want to release in the back-end, you can't do that now independently, but maybe you have to release the whole front-end at this time. So, this
somehow didn't solve the problem. Currently, a lot of companies are doing micro front-ends. That's not so easy because there are no mature frameworks at the moment depending on JavaScript library that you use. So, probably there is an issue. But today, I'd like to talk to you to a completely different architecture style. It's called self-contained systems. And there's a website, scs-architecture.org. The architecture and the website is more
or less maintained by a company called InnoQ. And there was an architect at this Stefan Tilkov, and he introduced that architecture at the same time more or less as Netflix microservices. He was working for a big retailer in Germany, and they tried to split the application into modules, thought about differently about architecture definition, and that's why they had a different idea about that. So, what's the idea?
First of all, the idea is we want to split a big application into smaller parts. And there, also self-contained systems says, "Okay, domain-driven design bounded context is probably the way to go. And then and that's the different to we wrap each part or each module into a full application. So, we don't just create a backend service, we create the whole application. And that's then called the self-contained
system. That's the idea. Uh I'm very curious who heard about self-contained systems before. That's what I thought. So, it's unknown. And at least here in the German-speaking part, it's completely different. A lot of people are talking about self-contained system, but that's probably because the creators are from Germany, right? the most important part here is in a self-contained system, you have all three layers. So, you have the
user interface, you have the business logic and the database in one silo. And you can have an optional API if you need that. Preferable not uh synchronous. And this already gives you an idea how big a self-contained systems could be because this will be much bigger than a microservice because we try really to cover a full uh bounded context. And then probably the communication between the services
is not that important as you have it with microservices. As we have it microservices, it's very important that each self-contained system has its own data storage. But because we are trying to avoid communication between self-contained system, we end up most of the time with data redundancy. Uh we will see about I will talk about That's not as bad as you may think of. But what's very important
is that only one self-contained system writes a certain data object. Right? So, this must be always be just one. Now, the question is how do we integrate self-contained systems? As we don't build something like uh micro front end or something, we more or less have one way. And that's the good old links. Right? So, we really navigate from one self-contained system to another just by hyperlinks. that's
good enough because usually if you really follow that bounded context way, you will see that only uh or most of the users of your whole applications only uses one self-contained system. But I will talk to you about that in a minute. Then communication I was also to talk about because you want to have really independent systems, we avoid to have synchronous APIs. So, that means we have
maybe events or we have messaging as communication But we will have maybe some eventual consistency. Right? So, we have to somehow deal with relaxed consistency, but also there that's not a trade-off that you may think about. If you look at the advantages of self-contained systems, no surprise these are the same as with microservices. Right? So, we are because we are loosely coupled. The systems can be replaced.
And probably that's the most important part of all modular uh architecture styles because for the last 7 years or so, I'm mostly working in modernization projects. And the biggest problem there is they are all monolithic. And first we have to somehow split the monolith that we can modernize uh that into modules. And if we had self-contained systems or even microservices, we could just replace that, right? And
because they are also um independent, uh we don't have big bang releases. We could even choose different technologies per self-contained system, front end and back end, because we are completely free. And we can scale self-contained systems individually. Uh sometimes when people talk about scalability, they just talk about uh scaling up. So, add more services to serve more users, for example. But I'm mainly doing business applications, and
there we need the other way round. We want to scale to zero because very often these business applications are only used Monday to Friday during office hours. That means at the weekend and in the night you can just shut down. And this is more sustainable because it could save maybe uh some energy, for example. But this only works if you have a platform that uh supports uh
scaling to zero. And finally, self-contained systems are open, means you can also buy uh systems and integrate them. Then, as we are in the age of AI-driven software development, we also have a benefit of self-contained systems. So, if you have uh monolithic applications, you may have a problem because the monolith is too big and will not fit in the context for your AI H it. And then
you have to split it somehow and and work with that. If you have a microservice architecture and I'm working currently for a customer in Switzerland, they have around 500 and we try to do spec-driven development with that customer and now we're trying to figure out how the microservices fit into this spec-driven thing. And that's not as easy as you might think because if you have many microservices
like in the image, it could be that you don't have all the necessary microservices in your context. And then you need to find a way how to share for example the API, but the API most often is not enough because it's not clearly described what the API really does. So, there we have kind of a trade-off with microservices and there the self-contained systems can help as well.
By the way, I'm having a talk tomorrow at 3:00 about spec-driven development. If you're interested in that, I can go into details why these self-contained systems are very very good fit. Now here, as we see the full picture, if you're doing spec-driven development, for example, you use user stories or use cases and specs, then you have everything in place. So, that means you can do the whole
application from front end, back end to database and that's great for AI because if you have a split between front end and back end, it's slower and more difficult to do. Maybe how you split it, it depends a bit. So, that means for AI probably a single stack with a single application is probably uh a good thing. So, that's what self-contained systems are and for the remaining
half an hour, I'd like to show you how we do that in a customer project. And this project is a modernization project for the largest wholesale company in and they have a homegrown ERP systems. So, their ERP system, the core dates back from the '90s. This used to be Cobol on Unix before. Then, they switched around 20 23 years ago to a very popular technology. Back then,
they used J2EE with JBoss and in the front end, they were using Eclipse RCP because Eclipse was very popular around 2003, 4, 5. Many companies were building or were using Eclipse as the framework for doing business applications. But as you can imagine, people don't want to use applications that must be installed. The UI is more or less a bit outdated and so, we need to create kind
of web applications for that. And just give you an idea, the wholesale domain is similar to the retail domain. So, we have like four subdomains. So, it's the core. That a domain that's called procurement where we manage suppliers, where we buy the products. That will then be somehow sent to logistic. That will deal with the with the products in And finally, we have sales like e-commerce where
we have the customers that can buy at goods. And finally, we have some finance stuff. That's not fully self-done because we have SIP because finance is usually a bit more difficult. So, that means we have these parts. And what we did, we were first uh splitting the monolithic application into that high-level sub-domain structure. And then we started to create uh something called uh bounded context. And that's
just to give an ID. So, we have way more self-contained systems. We >> [clears throat] >> probably will have around Sorry. We will probably have around uh 30 But here, just >> an example from an e-commerce system where we have an inventory system, we have customer we have order management, then the logistics system. all of them are self-contained systems. And what we need now is usually data
from other services. So, for example, if I'm in the order management system, I need product data because I want to show that in the web shop, for I need customer data because customers, at least in the wholesale domain, they may have special prices. And how do we do that? We replicate the data. So, you see there the three self-contained systems on the right-hand side have two not
two databases, but two schemas. One is used for the replicated read-only data, and one is the data that the system owns. on a regular base, depends a bit how you We will replicate the inventory data into order management system and also into logistics system. data replication is good enough because the change are the data doesn't change that often, right? So, for example, if someone creates a new
product in the inventory system, it takes around a week until the customer can order that. Because they will negotiate on the price, and then they will order the product with supplier. This will come to the warehouse and then it's to be ordered, for example. But we also have asynchronous communication, especially for data that we need immediately. And what do we need immediately? For um we have uh
the order management system and the user creates an order. Then this order is replicated or sent to an order topic or the event is sent like create order is the event and the data is sent. And the inventory will get that to update the stock, right? There's missing an arrow I see because the uh logistic systems also need that event because it will then start to prepare
uh for delivery, right? And once the order is ready for delivery, you will get an event in the logistics topic then that is used by the order management system to tell the customer, "Hey, your order is on its way." For example, so that's the two kind of communication uh ways we have. So, we mostly do data replication. and for live data we have topics that we use
to publish events. But that's not really event sourcing or something. These are really just events that end up in a queue and some consumer will do that and update the data. So that's the idea. what we also did is so as I said, self-contained systems are completely independent. That means we could use the four self-contained systems and in one we use JavaScript, in the other .NET, in
one Java and in one something completely else. It really doesn't matter. But we decided because we want to exchange developers from teams that we will build all the applications with the same framework stack and same language. So what we did, we defined this data integration that I just was talking about. So the replication of read-only with mainly views or materialist materialized views. And we have asynchronous communication
through events. On the other hand, we need a UI. And that's just an extract from an example. Uh we need a design system. So we want to make each application look the same. So because we don't have a monolithic front end, we each self-contained system has its own views. and we want to have that as look as similar as possible. But as I said before, if you
look at these four systems, we don't have the same users in these systems. So that means if you look at logistic system, we have people from the warehouse mainly working with that. They will not see the other systems. Or if you have people doing dealing with products are called category manager and the category manager just works with the inventory system. So, they will not even see the
other applications. So, it wouldn't even really matter if the application doesn't look exactly the same. But, we did a design system with components and styles. And what we also have is a linked database because we have multiple stages. And if you want to navigate from one application to the other, we need to know at which address uh the application actually runs. So, the ID of these two
six. again about this data redundancy. As I said, there are more or less two ways. Either you do replication or you do events. Uh the difference is in the way it works, right? So, because replication is done pull-based, that means the system that needs data from the other system is pulling that. And we have two mechanism. I was already talking about materialized views. So, we have the
users access to the other system and then materialize the data. So, that's more or less kind of a database-driven approach because we're using Oracle databases for all the systems, so we can do that. If we didn't have that, if you had different database systems, for example, then we would need a database replication mechanism. And in fact, we have one. Because this uh wholesale company also have stores
in Switzerland. And the stores must work independently. So, even if they don't have internet access, they should be able to sell. And there we have something um or some tool that was initially created for uh the Dollar General Markets in the the States. Uh that's even open source. Uh, and that can be used to replicate data over HTTPS site to another. the other difference is that uh,
with events, you have a publisher. So, the publisher tells you when this happens. On the other hand, we have again pull-based approach. And as I said, replication works best for data that doesn't change often or data that changes often, but it's not relevant if it's not actual. So, that means if you have uh, relaxed consistency, for example, with the eventual consistency, you can go with the replication.
That's not a problem. Because depending on the data, we have quite a long delay. So, some master data, for example, we just replicate once a day. Other data we replicate every minute. So, it really depends uh, what the data is, and you should really uh, think about that because this will reduce uh, load if you just do it as few The good thing about replication is that
this very simple. Either you do it like we did with materialized views, or you use a tool that uh, can do that. On the other hand, uh, the events are good if you have kind of near real-time updates. Like I said, we uh, send your orders somewhere, or we need uh, the information about the picking from the warehouse. These are typical events that we have in our
But be careful because this has higher operational complexity. Means you have someone else to manage, the messaging system or the Kafka cluster or something like that. You will need. Events could have a benefit that we don't use because you could store these events. And then you could use the events also for data replication later But because we are currently relying on database replication, we don't do that.
But maybe in the future this could be a a good idea. And if you use something like an event stream like Kafka, you can replay old events and then you can update the data. Maybe from there. Now, as I said, we are doing migration. That's the current state on the left-hand side. And we use a so-called strangler fig pattern. I That means we are extracting things. So,
the idea is we have at the end self-contained systems. We currently actually don't know how many we will end up. This depends a bit how we split Because we started with the most important parts of the system. So, we try to keep the old inventory and customer management system as long as Because as you can imagine, in an old system, not only the application is accessing the
database. We also have data warehouse and we have other external systems that are directly using our database. And especially for the master data, that gave us a few headaches. So, we try to keep these applications for a long time. But in the meanwhile, we started with the order management system. And if you have that, we move the UI or we recreate the UI in AI-based way. And
what we have as an intermediate phase, for example, are dashed red lines. So, that means at some point maybe the old application directly or already uses the new business logic. And sometimes the new business logic uses the old database. The second one we mostly do because we have for the order management system the idea that the new system and old systems works in parallel. So, we want
to reduce the risk that when we roll out the new system and something users can just use the old system. And that's why we also write currently on the old database, but this has to be gone. So, that means we go piece by piece move the old application to new applications, deploy the applications. Uh we have an OpenShift cluster where we run applications, so they will uh
deploy there. And currently we have like five cell content systems that we are working And um >> Oops. >> we start or we plan to go live in 2 months around with your order management But, the project will take a while. We don't know yet if we are really fast with AI-driven approach because I already did the similar migration a few years ago. And point is uh
migration of a big system usually takes way longer that you think. Not because of the programming effort, but because the people that you need in uh doing the modernization or helping with modernization are the people that know the old system best. So, if something happens in the old system, they need the same people as you need to do the more modern system, and this um makes such
system go very long. So, I modernized the new ERP product uh a few years ago uh from Oracle Forms to some web application and this took around 5 years. And we also planned with around the same time. So, management currently thinks it will be done in 3 years, but internally we plan with with 5 years. Now, this triangular fit pattern Martin Fowler once uh saw this plant.
So, on the right-hand side we see that when the plant was already done. So, what happens here is you have a plant that lands on a tree and then it starts to grow. And it wraps around the tree and so at certain point the tree in side the plant just dies. And as you can picture on the right-hand side, it's completely gone. And that's a very good
uh uh illustration of what we are doing with with the migration. So, that means probably some parts of the application will stay for a very long time and others are already renewed way before. That also helps to reduce uh the risk because we have uh less big applications By the way, how we do that? Uh we don't do a migration from JBoss EJB's to Spring Boot components.
So, we use Spring Boot in the new application, we use JBoss in the old one, we use uh a UI framework that's also uh Java, it's called Vaadin, which fits very well for um business applications. We also have React for the commerce system. That's not on the picture here. But we don't translate the code. What we do, we reverse engineer the requirements and then we discuss with
the people from the business, with the stakeholders, if these are still the requirements that they need, and then we start uh uh uh migrating the code or generating parts of the code. And that's a very interesting fact because we started the project 2 years ago. So, I'm the architect in that project. And from the beginning, management said, "Don't add new features. Just move the old system to
a new technology. And then once you're done, you can start to add the features." But in the meanwhile, with the process, we already started to implement new features. And this gives uh very positive view on the user side because they now finally get features they were waiting already for many years to get them implemented. That's very helpful, but more about that uh tomorrow in the other talk.
So, now I'm a bit too early, but to conclude, um in my opinion, the optimal modularity is always based on business boundaries. So, if you do modules in your application, you always should check uh how your users are using the application, how the domains look like, how you could split out these bounded contexts to really reduce communication between bounded contexts. This will help help a um that
you don't have any communication between the systems. What also helps now is or always helps is team autonomy because you can imagine that uh monolithic application that we currently have, we always have problems, especially with database migrations, for Which is the owner of the database is uh a problem. Then we have less run time dependencies because something I didn't talk about this we have big bang releases
with the monolithic applications currently. So we have to do that usually at Sunday night when not a lot of people are working with system and if something happens we have to have a hotfix and wait until the evening or if it's very important we have to shut down the system. And if you do the self-contained systems correctly, we will end up in a system that has uh
services that also have the availability that is required only, right? Because the order management system we can imagine that must run uh every day, seven uh days a week, 24 hours. But the inventory system is something that the people only use Monday to Friday during business hours. And if the system doesn't work, it's not so bad, right? So they can go and drink a coffee or do
something else. But they we don't lose any money or the customer doesn't lose any money if the inventory system doesn't work. But he will lose money if the order management system doesn't work. So we have different availabilities. And finally we have very simple deployment now because we really can uh just deploy that. uh if we do the next modernization will happen in a few years, I'm sure.
Then we can continue that by just modernize self-contained system by self-contained system like the same way we do that now and this will be much more sustainable. So that's it from my side. Thank you very much for coming and have a nice rest of the conference. >> [music] >> Hey.
More from this event
See all 126 talks →
AI Is Not the Risk. Architectural Drift Is - Sunil Kalkunte
17:39
Breaking the Monolith: Tesco’s Journey to Federated GraphQL with xAPI - Vishwas Chandrashekar
29:13
A Practical Introduction to LangChain4j - Venkat Subramaniam
1:01:28
Beyond the AI Models: How Lowe’s is Building the Store That Knows - Swaroop Shivaram
13:59