Many companies are using microservices these days,...
# feed
e
Many companies are using microservices these days, but for many smaller companies, the complexity that comes with a distributed system is out of the question. However, one of the primary benefits of microservices, decoupling, can be realized in a monolith. I created the following project to bring some of the advantages of microservices to monoliths. I’ve called it “microlith”. It is my magnum opus of architectures. I’ve invented nothing here, but combined a number of interesting concepts in a way I think is novel. I’ve taken the concepts of Domain Driven Design, Functional Programming, Algebraic Data Types, CQRS, Hexagonal Architecture, Reactive Systems, etc, etc and combined them in an elegant way to create an architecture for backend applications that solves many of the issues that arise from traditional “pancake style” (web tier / services tier / repository tier) architectures. When used properly, this architecture provides better guardrails for “good developer behavior”, as well as providing a framework for moving various business invariants from runtime checks to compile time (thanks to ADTs). Please take a look. I’d love your feedback. https://github.com/edreyer/microlith Oh, and this is 100% kotlin. This architecture is, for all practical purposes, not possible in Java. Update: Thanks for the comments! A clarification: This is a monolith. Internally, the code is structured so that each Bounded Context (DDD) is segregated. Communication between contexts happens internally using some lightweight CQRS-like tooling, but does not use the network (e.g. no Kafka required).
❤️ 2
👀 3
Each bounded context (BC) is isolated both at compile time and runtime. At compile time, this is done by segregating each BC into a separate maven module. At runtime this is done by encapsulating each BC into its own Spring Context. See the
booking-bc-example
branch for an example of how you might add a new BC in your own domain.
a
Looks interesting. And there are some similarities (at least in terms of motivation) with device control systems we are researching. Are you interested in doing a talk at https://www.meetup.com/KotlinMoscow/?
j
I wouldn't call anything involving both kafka and spring micro. I use quite a bit of Spring and uncharacteristically without a relational database. We're actually considering switching to Ktor as kotlin native is starting to look like that might work with that eventually. One important consideration for us is faster startup and lower memory usage.
m
Microservice arch deployed as monolith
is it what's called "distributed monolith" ?
k
I've always thought of microlith as backend + web frontend but this an interesting definition. Nice use of archunit too...haven't seen it used in a real project before but it's been on my list of things to try 🙂
t
@deviant No the distributed monolith is when you have a monolith deployed over distributed services. Here, the services appear to all deploy in the same place. @Erik Dreyer What benefits does this project have over simply inverting dependencies with interfaces, segregating code using regular library modules, and deploying everything inside one binary without involving kafka/spring before they are needed?
e
Thanks for the comments! To clarify, this is a monolith. Internally, the way in which you structure your application is such that it’s like you’ve bundled microservices together. The idea is to gain the benefits of microservice architectures, namely segregation of duties, but without having to have a distributed system, or rely on messaging systems like Kafka. One potential upside to this is that when you are actually ready to implement a real distributed system, it is fairly trivial to move each bounded context out of your monolith, and into your microservice. There is no hairball to untangle.
There are many other benefits to this approach. To understand those requires some knowledge of Domain Driven Design and hexagonal architectures. I encourage you to read up on those topics if they are new to you.
The project README touches on some of this, and documents, at a high level, the issues that arise from more traditional layered approaches, and how this solves them.
t
One potential upside to this is that when you are actually ready to implement a real distributed system, it is fairly trivial to move each bounded context out of your monolith, and into your microservice
I see this as one of the primary benefits. Because it’s already being tested via spring, its probably more likely to move out to a microservice without issues. However, I don’t think this will actually help with the hairball part of the problem. It’s still easy to make a hairball with fully distributed services. Hexagonal and DDD help, but I’m still unclear how this project helps implement those. By nature the implementation of these are domain specific. How can something that’s not domain specific help?
👍 1
I think you’d be more likely to be able to deploy - but not more likely to design a good architecture
e
Hexagonal and DDD help, but I’m still unclear how this project helps implement those. By nature the implementation of these are domain specific. How can something that’s not domain specific help?
Right. When you compare a system like this to a traditional 3 tiered, layered approach (controllers / services / repositories) there are much better guardrails for how you structure your business logic. For example, in the traditional approach, use cases are implicit. The logic is spread throughout a variety of Services. It’s been my experience that it can even span multiple layers. Services over time become very broad. I’ve seen Service classes that autowire in 20 or more other services, DAOs, etc. This approach doesn’t prevent this, but it provides some mechanisms/guardrails to better reason about your domain and structure it in such a way as to prevent this. More here: https://reflectoring.io/book/
t
Oh yeah, I was probably less clear. I love and use Hexagonal and DDD. Both super helpful. I’m just unsure how microlith provides helpful guardrails. In my experience, these things add friction which can be both positive and negative. When working in an environment that would have otherwise produced good architecture, the friction makes it harder to do the right change. When working in an environment that would have otherwise produced poor architecture, it can produce friction that prevents negative change, but also friction to prevent positive change. I'm not a fan of needing to add inheritance to validate my architecture. That tends to add rigidity to the code. I'm also not a fan of adding these kinds of hard boundaries too early. One of the benefits of hexagonal is that you can start off your module small, with no layers, no ports, no adapters, etc. And as it grows, gets more complex, etc, you can invert dependencies as it makes sense. Good architecture should allow you to make as little decisions as possible. This feels like I'm committing to too many decisions, too early.
e
Fair points. One possible (and valid) approach to use this would be to put everything into a single bounded context to start. As you learn more about your domain, you can choose at any time to split part of the domain out into a 2nd BC (3rd, etc). I’d only caution a team using this to stay mindful of how entangled various aggregates become inside that single BC.
I’m a pragmatist. I agree with most if not all of your comments. At the same time, I’ve been a part of many teams over the years and seen virtually every project suffer from the same sort of issues. Issues that I tried to address here. My goal here was to try and produce something that helps teams avoid these common problems. You’re certainly free to “color outside the lines”, or ignore various mechanisms that I’ve introduced here. You referenced some ideas as “friction”. It’s true that there is more ceremony to get small things done, particularly CRUD, if you do choose to follow the pattern here. I generally see simple CRUD operations turn complex over time. Side effects are added. Various business rules get added. The mechanisms this framework provides are intended to help guide the engineer to make better decisions around structuring those new bits. I’ve seen what happens in projects that lack any such mechanisms.
2
To be more concise, this project doesn’t enforce the mechanisms. You can still use this project and build what amounts to a traditional 3-tiered application. But if you want to use some or all of the mechanisms, they are there for you when you’re ready for them.
t
All good insight, thanks for responding to my feedback!
e
Thanks for your comments! I appreciate the dialog.
❤️ 1
t
I like starting my new features as 1-tier to start. No database, no api. Ok maybe two, because separation of UI from logic.
k
btw more common term is modulith
3
e
I was wondering if there was a term. Thanks! Now I have to go rename it.
e
Interesting project, having a better look tomorrow! I created something somewhat similar, with the main difference being; introducing hard boundaries enforcing the separation of business logic and adapters. https://github.com/ESchouten/CleanArchitecture
👍 1