0

I'm fairly new at system architecting and I'm looking for some advice. My company is revamping its order management system. Currently is a monolith system that scales very poorly and is difficult for various teams to work on at the same time. I am reworking this to be a microservice architecture. My initial team will be creating the full system ourselves initially. And other teams will likely be maintaining them after the initial release. We'll be setting the initial code conventions and API architecture which the other teams will use to maintain it.

I've read a lot about the various different architectures out there. And I really like the Clean(Onion/Hex) architecture. Just where the dependencies point inward and the separation of concerns really makes the unit testing later on very easy. I've used this on various smaller projects already and my team is familiar with it. However, I'm unsure if this pairs well with microservices.

Our initial diagram for the services has around 9 services with 46 class libraries.

An example service would look like this

Users <- folder
--Users.API
--Users.Infrastructure 
--Users.Contracts 
--Users.UseCases
--Users.Domain

I would really love some advice from some more experienced architects. Is this overkill for a microservice? Should I instead replicate these libraries into a folder inside the API? Is there any better ideas on how to do it? It feels wrong to have so many projects in my solution. I have an internal drive that makes me want to squish them together.

Edit: What I'm really asking here is if this architecture works well with microservices in general. Does my service structure make sense? The sheer amount of projects is larger then anything I've touched. I can scope per project but it's early in development and with things changing a lot I end up having to debug with all of them at the same time more often then not. So I bring them up with a docker compose profile.

5
  • Does this answer your question? Architecture / Structure / Design of 1 Microservice
    – Doc Brown
    Sep 18, 2023 at 20:52
  • You can realize encapsulation in different ways, depending on your programming language. You probably can put all one microservice in one project, but still encapsulate API, and other "layers" on code level. Same goes for shared code. A microservice is one way to share a functionality, a compile time library is another. Choose what is adequate to your situation. Sep 18, 2023 at 20:57
  • If your users service can scale in its own, then you correctly identified a service.
    – Ccm
    Sep 18, 2023 at 21:41
  • Did you find the real reasons behind the performance and development issues in your current software? To prevent those the least thing you should find out before going a totally different way in architecture. If those problems are not specific, clear and understood there is no reason those won’t reappear in the new solution while adding new problems as well. It may also give strong insight in how to solve those issues. Sep 18, 2023 at 22:22
  • Our old system is a client-server which had its database switched out for a cloud database. So the problems are fairly evident. We're really starting from scratch for the cloud system.
    – ArcSpark76
    Sep 18, 2023 at 23:07

2 Answers 2

3

However, I'm unsure if this pairs well with microservices.

It pairs orthogonally. By that I mean that how you structure the internals of an application (whether it be a microservice or a monolith) has no bearing on how you structure your microservices to interact with each other.

You're asking us how to design your restaurant's kitchen by showing me what the dining area is going to look like. Sure, there's some expectation that if one is recently renovated and modern, so is the other, but that's a very loose expectation.

I like Clean Architecture, and I'd use it. I like microservices, and I'd generally favor using them. Both of those statement hinge on them individually being the appropriate for the use case at hand.

I could review the CA that you're looking at but it seems a tangential point to make based on the question being asked.

Our initial diagram for the services has around 9 services with 46 class libraries.

  • I am unsure what you mean by "class libraries" here and how they're counted different from microservices.
  • I would also advise caution with trying to immediately model the old application's entire feature set.

I make both of these statements for the same reason. When you're used to working with a monolith, it's very easy to mistakenly create what's comically known as a distributed monolith when moving to microservices.
A distributed monolith means that you're spreading the business logic across multiple deployed services without redesigning the interaction between components to actually promote microservices with truly independent lifecycles.

This isn't a guarantee. Maybe you really understand microservices and won't fall into this trap. But based on the question you asked, it seems you are still getting to grips with microservices and I'm going to make a judgment call here that you should tread very carefully.

My suggestion is to design one microservice at a time. For example, model all the customer behavior that you want, and create a service. Don't even think about the orders or the products yet. Finish your people service. When done, then (and only then) think about the product service. Don't think about the customers or the orders. And so on.

If there are multiple devs working here, all getting to grips with it, I would suggest pairing together on the first microservice so everyone's on the same page about this new way of working. Then, when at least the groundwork has been laid, other devs can look at other microservices in parallel.

However, avoid cross-contamination at all costs. If the topic of discussion is Clean Architecture in general, that's fine. But if a products dev is needing to talk to a people dev in order to get their code working, that's a very big red flag that you're creating a distributed monolith.

Is there any better ideas on how to do it?

See the previous section.

I have an internal drive that makes me want to squish them together.

Yeah that's your monolith habits trying to manifest, I reckon.

Should I instead replicate these libraries into a folder inside the API?

I don't understand your goal here but I worry about what you're trying to carry over from the old world into the new world. I advise you to revisit your design from the ground up instead of carrying something old along for the ride.

Is this overkill for a microservice?

When you ask about overkill, what you're focusing on is the effort of getting this set up. Generally speaking, a more productive question would be "how can I minimize this effort?"

You could set up an empty project template so that you don't have to design this base architecture over and over. For my current company I created a dotnet template that takes in a -name parameter and spits out an entire solution (with empty projects, project references between them, and default packages that we use) using that service name in all the right places. It takes me about 30 seconds to generate the entire framework, and 15 of those are spent opening a command prompt.

It feels wrong to have so many projects in my solution.

Microservices are all about independent deployability. Generally, I will advocate for separate solutions for separate microservices but that's not for a technical reason.

What matters for microservices is the discretely deployable components. How you manage your source code (git repository wise, solution wise) is relatively irrelevant as to what makes a microservice a microservice.

10
  • By class libraries, I mean layers really. Each layer I mentioned above is it's own library technically. Domains, UseCases, Infrastructure. Then API which is a console app, the actual api. My first take on microservices had many services sharing libraries. But each service had its own namespace in the project. So I'd have one domain library for all my services and there'd be a folder in that project for each domain. i realize that's kind of anti pattern for microservices. So now my 9 services each have their own individual layers. But the project really ballooned up
    – ArcSpark76
    Sep 18, 2023 at 23:01
  • Not to say that it's bad or anything. That's why I'm asking the question. Does it make sense for each service to have it's 4 layers as separate libraries. Is it normal to have 50ish projects in the same solution. Is that okay to do? Are there performance issues for having so many libraries?
    – ArcSpark76
    Sep 18, 2023 at 23:04
  • @Arcalise76: (a) Honestly I don't register this whole library train of thought and you don't need it to work with microservices. I would delete that train of thought because it's going to get confusing between this and actually packaged libraries that you might use. Simply put, I suggest to stop using the word "library" when thinking about a layer/component. (b) Don't worry about the ballooning. Clean separation takes more space because the separation is a non-zero amount of code/structure. Especially in the beginning, when your projects are devoid of most business logic. [..]
    – Flater
    Sep 18, 2023 at 23:34
  • @Arcalise76: [..] But think of it like building your city with wide roads before all of your population moves in. (c) I have to unpack your questions and sort of rewind them, and the general feedback is genuinely "stop thinking about libraries". What you're bringing up is mostly a distraction from the real focus. I would also advise to scope your solution to only contain one microservice; simply because it seems you are flirting with the risks of creating a distributed monolith.
    – Flater
    Sep 18, 2023 at 23:45
  • I've looked at it for awhile like this. I work a lot in the DDD world. You dont want to pollute your domain. Aggregating between domains is where it has gotten hazy. If my understanding is correct, Its not possible to do this in standard microservices. Because each individual service handles its own area of concern. So if, for example, i needed to bill an account, then decrement the inventory. In DDD i would have an aggregate that did this. But in microservices my business logic would call the services required instead of doing it itself. Does that sound correct?
    – ArcSpark76
    Sep 19, 2023 at 0:16
2

I don't think you've given enough information to really give specific guidance here. At a very high level, the main thing that I would note to anyone breaking down a monolith is to avoid building a distributed monolith. That is, an architecture with all the disadvantages of a monolith and all the disadvantages of a distributed application.

The term 'microservices' has started to lose meaning for me and I'm quite sure it doesn't mean the same thing to everyone who uses the term. I'm hesitant to even try to say what I think it means. So, I'm going to weasel out of that and explain what I think you should do. Call it microservices, call it something else, I don't care.

The really important thing when breaking down a monolith is understanding how the various parts are dependent on each other. The reason for that is that your new design needs to be centered around autonomy over data. A big mistake that a lot of people make is to create two 'microservices' that share a database. It isn't completely pointless to make these independently deployable but if you think that you've decoupled the two services, think again. They are inherently bound by sharing the same data storage.* For example, if you have a service that creates orders, one that updates orders, and one that retrieves order data, they are inherently coupled by the fact that they all need to understand orders the same way. I'm not going to assert that it never makes sense to split these out, but I would argue that the starting point should be to assume that all of these operations are part of one 'system' or 'application'. If you change the data or database structure, all of these are likely to need to change in some way. There's just no getting around that. I'm sure you can find articles explaining complex strategies to avoid having to share a DB but for most teams, this will be both a waste of time and also a source of struggles that are similar, if not worse than what you are dealing with today.

The trick here, IMO, is to find the natural 'cracks' in your current monolith much like a stonecutter will split a stone where it 'wants' to split. Which parts of the system are 'clumped' together? Cleave between those clumps such that your APIs that represent the dependencies between these subsystems are simple, straightforward, and unlikely to change. A common strategy for this is related to the concept of Conway's Law. The idea being that you don't want the Accounts Payables project to have to update same systems that the Accounts Receivables project needs to modify. (That might be a bad example, my accounting knowledge is novice at best.)

The big advantage to doing this is that you get the big value win: it's easy to understand what a change means in these more focused subsystems. You also have clearly defined the dependencies between subsystems. And also importantly, each subsystem can evolve independently. If a team decides Python is a good choice for writing subsystem A and another team decides C# is a good choice for writing subsystem B, your decomposed architecture will not prevent that. Another way of saying that is if you are planning to build 9 systems that all look basically the same but just have a different 'subject' they operate on, you are probably missing the point. You are definitely missing the point if these 9 systems are going to use the same DB.

*People have pointed out to me that you can have different applications in the same DB using separate schemas. I agree if you have strict enforcement of isolation between schemas. The key is: as long as a system can move to a separate platform or DB without impact on any other subsystem, it should be fine.

4
  • The 9 systems that look the same and operate on different subjects is a valid point. I was personally taught microservices have a very limited scope. so the area of concerns should be limited also. My understanding is, doing it that way can help with scaling the system. If Users is being hit hard, it can be independently scaled up as needed and that'd effect no other part of the system.
    – ArcSpark76
    Sep 18, 2023 at 21:42
  • At some point, microservices will need to know about each other and some common business rules. Completely abstracting the business rules is not productive. The problem with sharing a database is about scaling the database. A shared database usually comes with relations between tables belonging to different services, which in turn makes the database difficult to scale. The schema trick only works up to a point.
    – Ccm
    Sep 18, 2023 at 21:46
  • @Ccm "The problem with sharing a database is about scaling the database." This is an issue, but I don't think it's the primary one. "A shared database usually comes with relations between tables belonging to different services," That's the key problem that makes it hard to manage. E.g., you want to change your tables but there are all kinds of dependencies from other parts of the system that will need to be changed.
    – JimmyJames
    Sep 19, 2023 at 14:16
  • @ArcSpark76 "I was personally taught microservices have a very limited scope. so the area of concerns should be limited also" I hope you are not misunderstanding my answer here because that's exactly what I am saying. The problem is that people often take this too literally and do things like deploy each service independently but retain a shared datastore. You end up in a situation where when you change things, you now have many deployments to coordinate which is nearly the opposite of the goal.
    – JimmyJames
    Sep 19, 2023 at 14:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.