2

As the title states, I'm wondering if my current architecture is currently too granular. Additionally, I'm curious if I should be pursuing a microservice architecture in the first place.

For context, I'm building a multi-tenant document generation system and I currently have the following entities:

  • Tenant
  • Template
  • Document

In terms of entity relationships, there's a one-to-many relationship between Tenant and Template, as well as a one-to-many relationship between Tenant and Document.

As it stands, I essentially have a microservice (REST API) per-entity (which I'm now realizing can be a problem itself).

One problem I'm facing is how to communicate between the services, and more specifically, how that impacts the domain/context of all of these services. As I understand it, synchronous communication between services in an anti-pattern, which becomes a problem in my application.

In practice, when a user creates a Document (via REST API), I call out to the Tenant and Template services (via their REST APIs) primarily for validation purposes (i.e. "Does a Tenant with the specified ID exist? Does a Template with the specified ID exist?").

To solve this, I could replicate the data in an eventually consistent manner such that the Document service doesn't have a "hard dependency" on the other services, but this leads back to my initial question of whether the microservice architecture makes sense in the first place because all of these entities are closely related.

If I were to take this route, I'd effectively have three (3) data stores (DBs) for Tenant information, albeit two of those will be heavily stripped-down (an ID column and whatever information the specific service requires) from the data store used in the Tenant service. On the downside, this requires some extra effort to coordinate. On the upside, if new properties are added to the Tenant entity, the other services don't need to change unless they need to use these new properties.

For some extra context, there are other use cases other then the ones I mentioned before. For example, I plan on adding a web application to create/modify Templates in the future. If I kept the current architecture (microservices), I can scale the Template microservice up as the traffic (inevitably) increases.

4 Answers 4

3

When you are looking at monolithic design vs microservices, you are making trade-offs. The primary tradeoff is this:

  • Monolithic: a simple arrangement of complex things
  • Microservices: a complex arrangement of simple things

There are a number of other concerns to think about, from deployments to scaling, and minimizing the amount of damage when things start breaking.

  • The way you scale monolithic applications is all or nothing.
  • Microservices can scale the individual services separately
  • If a microservice goes down, you don't have to lose the whole application
  • The more moving parts you have, the more challenge you will have with your deployments

There are a number of very good reasons to go with microservices, and there are a number of good reasons not to. For example, Stack Exchange is not built with microservices. It does have a number of servers involved though (ref).

I'm not sure what your performance requirements are, or the size of your intended audience, but there are varying levels of microservice purity. The bottom line is that when you are at Facebook scale, Netflix complexity, etc. you need a more pure separation for your microservices. However, for smaller deployments that's not necessarily true. Some things that I am aware of some projects doing that isn't "pure" include the following:

  • Shared database
    • All services can read
    • Only one service can write
  • Messaging to notify when things update (eventual consistency) so they know to update local caches

Regardless, of whether you go with microservices or monolithic design, you do need to take care with how you handle user sessions, etc. so you can easily reroute traffic if a server goes down without losing anything. These days it is most common to use a token with all the user information encoded in it (JWT works well for this), and the front end passes that to the backend. As long as the token is passed with every request, there is no need to track user sessions on the server.

1
  • 1
    Nice answer. You mention the idea that the ideals of monolithic design and microservices are not the only choices. We should never limit our design to idealized (and therefore extreme) choices. Idealized architectures serve a purpose. They exist to be understood and to inspire and not to be just blindly copied.
    – joshp
    Commented Jun 15, 2021 at 3:09
2

I laid out my basic thinking in https://softwareengineering.stackexchange.com/a/264830/16864 and still agree with what I wrote there. You almost certainly do not need microservices.

As a data point, commodity hardware 20 years ago had no trouble serving hundreds of thousands of hits per day to millions of unique users per month. I know this because I did it. And today's hardware is better. Furthermore if you get to that scale, you should be able to afford a rewrite.

As a rule of thumb, if you've got a granular microservice architecture and don't have 50+ machines for each service, you probably aren't improving throughput over a single machine running a monolithic service. Most people who brag about how scalable their microservices are, are throwing hardware at a self-inflicted problem and live with unnecessary complexity and latency as a result.

If you really need it, you have to go microservices. But far fewer need it than think that they need it.

1

The microservices pattern is difficult. Very difficult indeed. Talented teams have failed to implement it, while others have simply given up.

So, wouldn't it be better to ask the question: "What do I gain by using microservices?"

It seems that unless the payoff far outweighs the costs of implementing such a pattern, implementing a monolith is still the best solution. Indeed, there is nothing to stop a monolith from separating its databases, or using patterns that allow objects to be decoupled so that only one place needs to be changed when one thing changes.

In the same way that patterns emerge from the code through analysis of the code, microservices impose themselves when we need them.

To use your example, perhaps document generation is resource intensive. So, we might find it interesting to separate this generation from the rest of the code in order to allocate more specific resources to it in order to reduce costs while improving overall performance. We would have "discovered" a really useful microservice and separating it from the rest of the code would greatly improve our architecture. On the contrary, if generation is cheap, separating this code from the rest would only add an unnecessary layer of complexity while losing cohesion and gaining nothing more.

1
  • In regards to your last point, it is resource intensive. That being said, I'm already offloading that work to another process using messaging queues (RabbitMQ in this case). Commented Jun 15, 2021 at 13:58
0

This is more a question related to domain-driven design, rather than microservices specifically.

IMHO microservices should span domains rather than entities - you've discovered the entities that make up your domain (documents and templates).

I think it is OK to push other concerns out to other services (tenants) - and use data replication (via integration/domain events) back to other interested services (think "TentantCreated" / "TentantDeleted" events).

This way your services are able to do "useful" work without becoming a rats nest of orchestration code.

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