0

Imagine we have a microservice M1 with an aggregate root called Player and a microservice M2 with an aggregate root called Classification, now in the M1 we need to do some logic based on some property from Classification, now some steps to do that are:

  1. Replicate the list of possible Classifications to M1 via asynchronous messaging;
  2. Do what is asked by the business in M1;

Ok, now imagine we have a view to add Players, and in that view is possible to choose the Classification of the new Player from a dropdown list. Now the question:

Should the dropdown list be populated with the Classifications that were replicated into M1 or from M2?

As you can see, by using the data from M1 we would have to expose the Classification from M1 via an API, thus the title of the question.

EDIT

The replications happens through async messaging using events, so I'm not exposing the entire aggregate to M1 just some properties like an Id and the Description of the classification.

Ariel Moraes
  • 602
  • 7
  • 15
  • 1
    What worries you so much about exposing data that was replicated from another BC? In your case, it would be read only, right? – guillaume31 Jul 31 '18 at 11:59
  • Yes it would be read only, actually I'm not worried about exposing it, the question is just to make sure whether or not it is a good thing to expose an aggregate root that is managed by another context. – Ariel Moraes Jul 31 '18 at 12:54
  • 1
    Why wouldn't it? Plus, at that point it's technically not an AR from the other context any more. Clients know which context they query so they cannot mistake it for the original AR. – guillaume31 Jul 31 '18 at 12:57
  • That leads us to another question, I follow the rule that the AR manages all the child entities, so only my ARs have repositories, in that case (assuming the Classification is not an AR), when a new classification event arrives, how I'm supposed to insert it If I don't have a repository? – Ariel Moraes Jul 31 '18 at 13:07
  • You just have to store classifications somewhere in a reference table. How you call the data access object has really no importance. – guillaume31 Jul 31 '18 at 13:17
  • The accepted answer for this [question](https://softwareengineering.stackexchange.com/a/124392) has clarified it for me when it says _If you need to fetch slaves without knowledge of the master, then you should think about your aggregates and your object model. Perhaps you need a second aggregate where the slave is a root and then you should have a custom repository for them_. That fits perfect in my scenario, because I have to fetch those Classifications without them knowing the Players. – Ariel Moraes Jul 31 '18 at 13:26
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/177105/discussion-between-ariel-moraes-and-guillaume31). – Ariel Moraes Jul 31 '18 at 14:09

3 Answers3

0

I believe both can be done and one is not better than the other. Either ways, you're making API call to M2 to get the classification. But this scenario is now telling you that the service boundary might be wrong - the vertical slice on the view to create a new player perhaps should be a single micro-service. Of course, you will add some fallback mechanism for the call to M2 so that in case of failure to M2, M1 doesn't fail completely (in this case, to create a new player).

Kaidul
  • 15,409
  • 15
  • 81
  • 150
  • Where those API calls would be made? I said the replication happens by using an async message and events. There is no API call from one microservice to another. – Ariel Moraes Jul 29 '18 at 05:27
0

The main idea when you design your microservices is that one microservice should not make any synchronous calls to any other microservice. This implies that every microservice should gather any required external state in an asynchronous mode, before it will answer to queries or execute commands. One way to do that is (1) by subscribing to events; the other way (2) is by periodically querying some exposed Read-model (see CQRS), i.e. in a cron job.

In any case, you should not expose the entire Aggregate, otherwise you risk to break its encapsulation by depending on its internals. Instead, you should publish its domain events (1) or create a specially designed canonical Readmodel that present the most probable model to the other microservices; something like a canonical Read-model; I would avoid this unless the domain is too simple, too CRUD.

Constantin Galbenu
  • 16,951
  • 3
  • 38
  • 54
  • 1
    I would disagree with the **one microservice is not allowed to make any synchronous calls to any other microservice** statement. It's highly opinionated and per se nothing prevents you from doing so, neither its that bad. But one should **avoid it**, because it causes dependencies between the microservices and hurts scalability of the system, after all some of the systems just require external calls (i.e. IdentityServer4 to resolve reference tokes (though its cached after the first hit) and revocations) – Tseng Jul 30 '18 at 09:21
  • @Tseng I disagree. Scalability in the context of distributed systems (so microservices as well) is implied so it's pointless to give an answer like "if you want to have a scalable system then ...". – Constantin Galbenu Jul 30 '18 at 11:25
  • 1
    Most Microservices in the real world never exceed the point where they need to scale up to more than one or two instances. The more beneficial attributes of microservices is faster deployment cycles (when you only fix an bug in a single microservice you just have to deploy vs the whole monolithical application), decoupling, easier to replace/rewrite a single microservice and last but not least polyglot programming (using different languages and environments for the microservices. Scaling is just one aspect and not even the most important one due to reasons stated above. – Tseng Jul 30 '18 at 11:39
  • @Tseng what about resilience? do you consider resilience as being important? – Constantin Galbenu Jul 30 '18 at 11:44
  • (Micro)services calling other microservices can also be resilient, there are plenty of libraries to handle that from Zookeeper in the Java World to, by referring to themselves i.e. `app://orders/order` and where that gets resolved to the service in the background wherever its running and you may have cases where the client expects an immediate answer, where a message bus isn't necessary useful. Todays web frameworks can handle remote calls pretty well w/o being a bottle neck (i.e. ASP.NET / ASP.NET Core/nodejs supporting async/await calls, where awaiting request won't block any threads) – Tseng Jul 30 '18 at 12:08
  • 2
    Just saying **not allowed** is just plain wrong, since it sounds like a rule, which doesn't exist. It's just your *opinion*. **not advised** or **should be avoided** would be more appropriate, one just needs to know the consequences. – Tseng Jul 30 '18 at 12:11
  • Let me clarify things a bit, I've said the `Classification` replication happens through async messages in the first place, so the question remains: if the data is replicated, the view should use the replicated data or the original one to populate a dropdown list? The dropdown list is part of a view responsible of adding Players. – Ariel Moraes Jul 31 '18 at 11:23
  • @ArielMoraes the replicated data – Constantin Galbenu Jul 31 '18 at 16:07
0

You should treat each microservice as a service boundary. Means no aggregates ever leave the bounded context. The reason for this is, if you do that your leak your domain knowledge into other bounded contexts and have a hard dependency on it. Any change on the aggregate will break any depending service.

If you need to retrieve data from other bounded context you should do that through an anti-corruption layer. In terms of Microservices that can be translated into DTOs. This way when you add or remove a property you don't necessary break the contracts with the outside contexts.

From the provided information its hard to tell if Player and Classification belong to the same context or not. Basically there's nothing wrong to compose your UI data from multiple microservices (i.e. displaying orders and delivery notes on the same UI form, whereas they come from different bounding contexts, namely order and logistics).

However if your Player model/aggregates directly depend on each other and neither of them can be used independently of the other one, then its very likely its part of the same bounded context.

Tseng
  • 61,549
  • 15
  • 193
  • 205
  • I agree with every word, but in my case `Classification` lies on another bounded context, because who defines the existing classifications is the Administrative area and the context where the `Player` just choose one classification from the list. – Ariel Moraes Jul 31 '18 at 11:18
  • @Tseng an anti-corruption layer is not the only [integration mode](https://www.infoq.com/presentations/ddd-microservices-2016) between bounded contexts. – guillaume31 Jul 31 '18 at 12:06