I have a doubt about Microservices Architecture. We are developing an ERP and there're several microservices such as Human Resources, Identity, Orders and so on.
We've implemented a shared domain layer for entities that are common for all those layers, including abstractions ( interfaces ) of Company, Location and some value objects.
My question is: What's the boundary of shared items for microservices and how bad is that?
In that case, Those shared entities would be the same for each microservice, so that help us to write less code BUT at the same time creates a small level of coupling.
Usually microservice architectures adopt a "share nothing" concept, which mean your code bases should be ideally separate. Yes, that will mean you will write more code but will keep your microservices more manageable, uncoupled and probably lighter.
Also, regarding the DDD-part do the question, you should really strive to keep well defined boundaries within your application, which means you shouldn't be scared to have "redundant" entities in different bounded contexts because the same concept usually mean different things to different domain areas of your application.
Keeping onto the "ERP" theme, you'd expect the "Order Placing" context of your application to have quite a different view on the "Product" entity than that of the "Tax" context. Keeping those in distinct contexts in different code bases will allow you to model smaller aggregates with a higher level of cohesion that will be way less coupled to the other constructs of your model thus, making evolve your microservices way easier.
My question is: What's the boundary of shared items for microservices and how bad is that?
Up until a few years ago it was complicated to get the boundaries a microservice defined because there was simply no agreement on how to archieve that, but Evans sorted that out a few years ago:
GOTO 2015 • DDD & Microservices: At Last, Some Boundaries! • Eric Evans
Microservices also follow the four tenants of SOA and the same 9 fallacies of distributed system are to take in consideration nevertheless their business scopes are different. Bear in mind that a microservice architecture should follow a Shared-nothing sort of architecture, so services don't really share entities, what they do is subscribe to messages, typically in a bus, and store local copies of the pieces of data they are interested in. This obviously introduce another concept called eventual consistency and depending on your business requirements,that might or might not if in your overall design.
Related
My question here is quite straight as mentioned in the subject.
However, please allow me to give some brief explanation here about my innocent thoughts.
I've been using Axon for approximately 10 months now. I used to design my project structure based on the Hexagonal architecture with two top level packages respectively for domain and infrastructure.
Furthermore, domain package will contain different domain objects (as explained in the DDD concept) such as follow:
Aggregate (this will be an Axon aggregate class).
Repository (in my case, this will be a Spring Data Repository interface).
Entity (in my case, this contains any lookup entity that i used for set-based consistency validation as written here).
Service Port (collection of Input and Ouput port interfaces).
Commands (representing Axon Command object).
As for Events, I used to put them on a different module that I compiled as a jar file, so I can share it to other developers whom going to use the same event in their project.
I've noticed recently that all of my commands and events were basically anemic models (an anti pattern that we should avoid).
Is there any good practice on this ? Or, Is it something that intentionally used by design ?
I've been thinking to put my Command classes within my Aggregate class (as an inner classes). At least by using this approach I won't end-up with having so many anemic models scattered outside. Any thoughts ?
Commands are designed to be behavior and input structures mirroring the external world. They don't necessarily mirror an aggregate's structure.
They are not even connected clearly to one single aggregate, at times. Enclosing them within aggregates can be a code smell because you are then thinking in terms of resources and UI organization, instead of transaction boundaries and entity groups.
You are also violating the open-closed principle. Changes in volatile layers like user interface and request structures will make you edit the Aggregate class, and that is not good design.
On a more general note...
At times, this debate of anemic vs. non-anemic (or dry vs. non-dry) can push you in the direction of premature - and incorrect - optimization. Try avoiding this trap because you will end up optimising at the code level, but your domain will suffer.
DDD and CQRS guidelines align with principles that help you keep complexity at bay over the long term. Things kept distinct and separate help you achieve this.
First of all, in DDD, your domain had to be free of any frameworks, just use pure language library.
Then, mixing Commands and Aggregates cannot be a good solution. I think Commands belongs to Port while Aggregates belongs to the Hexagone.
Finally, DDD highlights the discovery of the domain thanks to the experts. Did you do that ? If not, if you're only using the Tacticts pattern, you'll miss one of the most important part of DDD.
We try to split up our domain into bounded contexts with the goal to have a modular application design/architecture.
We did an enlightening EventSorming session which helped us a lot to identify bounded contexts and its aggregates. After the workshop every participant agreed on the bounded contexts we identified.
Nevertheless we feel uncomfortable as we fear our bounded contexts are still too large. EventStomring focusses on the domain events / process and that's the major building block we used to identify our bounded contexts.
We also identified aggregates like "Contract". Every contract nearly follows the same process, but the amount of data these contracts contain can differ massively. There are very simple contract types and contract types which include a lot of additional data and attachments.
Is it meaningful to declare another bounded context just because the aggregate's data is more complex?
Both approaches have their drawbacks:
Implementing all contract types in one bounded context might lead to a lot of if-Statements in the code in order to handle the differing data.
Extracting a new bounded context might lead to a lot of duplicate code just because some data differs.
Any suggestions / best practices how to handle this?
...domain events / process and that's the major building block we used
to identify our bounded contexts
BCs are not identified by processes, BCs are related to the language. Each BC has its own ubiquitous language (UL). A BC is the context in which a concept has meaning. Anyway BCs belong to the solution space. First of all you should explore the domain (problem space) and split it in subdomains, distilling the core domain. Then you model each subdomain. A BC is the context where a model lives. Ideally the relationship between subdomains and BCs is 1:1.
The process of discovering subdomains is iterative, and you will find them as you know the domain better, talking to experts. When you find a word that may have different meanings, or when two different words have the same meaning, then it means that you are crossing a boundary between BCs.
So, subdomains identification is not about processes, but about concepts and UL.
Is it meaningful to declare another bounded context just because the
aggregate's data is more complex?
No, you shouldn't create BCs arbitrary just because aggregates are complex. BCs are based on the UL.
Any suggestions / best practices how to handle this?
You should learn more about the domain (contract, types, etc) by talking to domain experts, and study your aggregate (transactional consistency)... Anyway, if you split your aggregate into anothers, it doesn't mean that they belong to different BCs, they still can belong to the same BC. A BC can have more than one aggreagate. It all depends on your concrete domain.
Bounded contexts have little to do with if-statements, so I'm not sure what you mean.
Bounded contexts are a semantically closed set of business functionalities. Basically your bounded context is well defined if it can execute its functions in complete isolation, even if the other contexts are not available.
Other than that, you can have any design inside of the context. I feel the amount of if-statements depends more on your class/code-design, like whether you use polymorphism correctly, interfaces, things like that.
But, to your point: You don't need to have everything perfect the first time. If you identified some valid contexts, you already did the hard part. If any context can be further divided, that could happen later anytime with little impact on others (since contexts are more or less isolated).
No specific business teams for different kinds of contracts
No dedicated dev team for specific kinds of contracts
Same ubiquitous language is used for all contracts
Every contract nearly follows the same process
These to me are signs that all contracts belong to the same business subdomain and should ideally be in the same Bounded Context - unless legacy or third party systems force you otherwise.
I'm breaking my system into (at least) two bounded-contexts: study-design and survey-planning.
There's a concept named "subject" (potential subject for interviewing) in the study-design context. We also maintain associations between subjects and populations in that domain.
Now, in the survey-planning, we also need (some) information about the subject (for example: for planning a visit, or even for anticipated selection of questionnaire, in case the population the subject belongs to is known beforehand).
So, I need that "subject" in both contexts.
What approach should I pick? Having a shared kernel, as explained in Eric Evans DDD book? I don't mind (at least for now) having the two contexts sharing the same database.
Or... should I go pure microservice? Meaning: those two can't / shouldn't share database..., and in that case I might have to go the mirroring / duplicating route through event passing: https://www.infoq.com/news/2014/11/sharing-data-bounded-contexts
Any thoughts on which one is better, for the above situation?
Thanks!
The context for microservices is distributed systems. In any other situation it would probably be overkill. Shared kernel will eventually split. That is usually the case. You may start from it. Nothing wrong with that. However, it will not stay there.
I recommend that you choose a event-driven solution, but not necessarily to use microservices. You could build an event-driven monolith in order to spend much less time on synchronizing the two models. When the application grows too big then you split the monolith into microservices. You could use CQRS to split event more the models into write and read. If you use event-sourcing things get even more interesting.
In my experience, with shared kernel, the models become god objects, one-size-fits-all kind of objects.
In my opinion, you have three entities:
study
survey
person
It is pretty intuitive to see that each of these is its own aggregate root. So then we are talking about inter-root relationships. In my experience, those are meaningful entities on their own, and cleanest and most future proof by far is to treat those relationships as independent aggregate roots.
The relationship between a study and a person is perhaps called TestSubject, and the relationship between a person and a survey could be called Interviewee or something similar. In another context, the person could be an employee for a company, and then the Employee would be its own aggregate root. Information that only relates to the relationship and not to the person or the study say, should be limited to this relationship specific aggregate root. This could for instance be the start date at which the subject started to take part in the test, and the end date (when he dropped out, if he or she dropped out prematurely, etc.)
As for storage, all aggregate roots should define their own separate repositories as interfaces and know only those interfaces, but the implementation of those interfaces is free to choose to use the same database or different ones, or even different kinds, local or distributed, etc. So this holds for these 'relational' aggregate roots as well. But you should almost force yourself to use different databases and preferably even different technologies (e.g. one EntityFramework, the other MongoDb) when you start with this, to force yourself to make sure your interfaces are properly defined and independent of implementation.
And yes, big fan of CQRS as well here, and Event/Command Sourcing as well. There are many light-weight implementations possible that allow you to upscale, but are very easy to get into and afford you almost completely linear (=predictable) complexity.
You can start with microservices that share a monolithic data source, but only use partial domain entities and value objects
What are DDD recommendations for inter-domain referencing design?
Should I try to connect them as "Matryoshka" (put one into another) or it is better to create upper-level "inter-domain" business service?
P.S. Crossing this smooth water, I was unable to find anything useful to read in the Internet, and have started thinking that for this kind of things exist better term than "inter-domain referencing"... Am I right?
DETAILS:
I have two models/business services.
Semantically first domain (A) is CRM with sell/maintenance process for our goods, second domain (B) is "design" data of our goods. We have two view points on our goods: from seller perspective and from engineer perspective.
Actually each model is effective ORM (Object-Relational Mapping) tool to the same database.
There are some inter-domain activities e.g. validations (e.g. sometimes we can sell things to smb. only if some engineering rules are valid).
From developer's point of view I have two clear possibilities (reference B in A or create new cross reference domain/service C ). But from designer perspective I am lost in understanding what kind of Business Service I have when I compose business logic from two different domains.
As far as I know, DDD has no strict rules for 'inter-domain' referencing. At the end of the day your domain model will have to reference basic Java or .NET classes. Or it may reference specialized date/time or graph library (aka 'Generic Domain').
On the other hand DDD has a concept of Bounded Context. And it has quite a few patterns that can be applied when you work at the boundaries of the system. For example 'Anticorruption Layer' can be used to isolate you from legacy system. Other integration styles can be used depending on how much control you have over external code, team capabilities etc.
So there is probably no need to introduce artificial glue layer if you just dealing with two subdomains in one Bounded Context. Might also be worth reading Part 4 of DDD book (Strategic Design).
UPDATE:
Based on the information you provided, it looks like you only have one Bounded Context. You don't seem to have 'linguistic clashes' where the same word have two different meanings. Bounded Context integration patterns are most likely not applicable to your situation. Your Sales domain can reference Products domain directly. If you think of Products domain being more low-level and Sales being high level you can use Dependency Inversion Principle. Define an interface like ProductCompatiblityValidator in Sales and implement it in Products domain. And then inject the actual implementation at the application layer. This way you will not have a direct reference from Sales to Products.
In addition to what Dmitry has already said...
I think of any code that crosses bounded contexts as application layer code. I would have that application layer code reference domain types from both contexts (and their repositories) but not have two domains reference each other. I think it's OK to have business logic in an application layer if it specifically crosses domain boundaries and is unit-testable.
If you really have a hierarchy, then it would be OK to have the the more concrete subdomain reference the more abstract domain. However, I would be careful if this causes you to need to have domain objects reference repositories of any type. Pulling objects out of of a repository is rarely a true domain concept. Referencing repositories is best done in an application layer that sits a layer above the domain model.
Of course this is all as much art as science. I'd try modeling a thin slice of your application a couple different ways and see what friction you run into with each approach.
I've always developed code in a SOA type of way. This year I've been trying to do more DDD but I keep getting the feeling that I'm not getting it. At work our systems are load balanced and designed not to have state. The architecture is:
Website
===Physical Layer==
Main Service
==Physical Layer==
Server 1/Service 2/Service 3/Service 4
Only Server 1,Service 2,Service 3 and Service 4 can talk to the database and the Main Service calls the correct service based on products ordered. Every physical layer is load balanced too.
Now when I develop a new service, I try to think DDD in that service even though it doesn't really feel like it fits.
I use good DDD principles like entities, value types, repositories, aggregates, factories and etc.
I've even tried using ORM's but they just don't seem like they fit in a stateless architecture. I know there are ways around it, for example use IStatelessSession instead of ISession with NHibernate. However, ORM just feel like they don't fit in a stateless architecture.
I've noticed I really only use some of the concepts and patterns DDD has taught me but the overall architecture is still SOA.
I am starting to think DDD doesn't fit in large systems but I do think some of the patterns and concepts do fit in large systems.
Like I said, maybe I'm just not grasping DDD or maybe I'm over analyzing my designs? Maybe by using the patterns and concepts DDD has taught me I am using DDD? Not sure if there is really a question to this post but more of thoughts I've had when trying to figure out where DDD fits in overall systems and how scalable it truly is. The truth is, I don't think I really even know what DDD is?
I think a common misconception is that SOA and DDD are two conflicting styles.
IMO, they are two concepts that work great together;
You create a domain model that encapsulates your domain concepts, and expose entry points into that model via services.
I also don't see what the problem is with ORM and services, you can easily use a session/uow per service call.
Just model your service operations as atomic domain commands.
a naive example:
[WebMethod]
void RenameCustomer(int customerId,string newName)
{
using(var uow = UoW.Begin())
{
var customerRepo = new CustomerRepo(uow);
var customer = customerRepo.FindById(customerId);
customer.Rename(newName);
uow.Commit();
}
}
Maybe the problem you are facing is that you create services like "UpdateOrder" which takes an order entity and tries to update this in a new session?
I try to avoid that kind of services and instead break those down to smaller atomic commands.
Each command can be exposed as an operation, or you could have a single service operation that receives groups of commands and then delegate those to command handlers.
IMO, this way you can expose your intentions better.
The most important things about Domain-Driven Design are the big picture ideas:
the ubiquitous language,
the strategic decision-making where you are adding value by working in the core domain (and insulating yourself from other nasty systems), and
the approach to making testable, flexible designs by uncoupling infrastructure from business logic.
Those are broadly applicable, and are the most valuable pieces.
There is a lot of design-pattern stuff about Factories, Services, Repositories, Aggregates, etc., I take that as advice from one experienced developer to another, not as gospel, because so much of it can vary depending on the language and frameworks that you're using. imho they tend to get overemphasized because programmers like us are detail-oriented and we obsess on that kind of stuff. There is valuable stuff there too, but it needs to be kept in perspective. So some of it may not be that relevant to you, or it might grow on you as you work with it.
So I would say it's not like there's a checklist that you can run through to make sure you're using all the patterns, it's a matter of keeping the big picture in mind and seeing how that changes your approach to developing software. And if you pick up some good tips from the patterns that's great too.
Specifically with respect to the SOA thing, I've developed applications that defer all their state to the database, which have used persistence-ignorant domain objects. Writing tests for services that have to mock daos and feed stuff back is drudgery, the more logic I can put in the domain objects the less I have to mess with mocks in my services, so I tend to like that approach better.
There are some concepts introduced with DDD which can actually confuse you when building SOA.
I have to completely agree with another answer, that SOA-services expose operations that act as atomic commands. I believe that a very clean SOA uses messages instead of entities. The service implementation will then utilize the domain model to actually execute the operation.
However there is a concept in DDD called a "domain service". This is slightly different than an application service. Typically a "domain service" is designed within the same ubiquitous language as the rest of the domain model, and represents business logic that does not cleanly fit into an entity or value.
A domain service should not be confused with an application service. In fact, an application service may very well be implemented such that it uses a domain service. After all, the application services can fully encapsulate the domain model within SOA.
I am really really late in this, but I would like to add the following in the very good answers by everyone else.
DDD is not in any conflict with SOA. Instead, DDD can help you maintain a better Service Oriented Architecture. SOA promotes the concept of services, so that you can define better boundaries (oh, context boundary is also a DDD concept!) between your systems and improve the comprehension of them.
DDD is not about applying a set of patterns (e.g. repository, entities etc.). DDD is mostly about trying to model your software, so that all the concepts (i.e. classes in case of object-oriented programming) align directly with concepts of the business.
You should also check this video (especially the last 5 minutes), where Eric Evans discusses exactly this topic.
I've even tried using ORM's but they just don't seem like they fit in
a stateless architecture.
I don't have any reference handy to back this up. However, you're right, ORMs do not fit nicely with DDD as well. This is because, they're trying to bridge the object-relational impedance mismatch, but in a wrong way. They force the software towards an anemic domain model, where classes end up being "plain data holders".
I am starting to think DDD doesn't fit in large systems but I do think
some of the patterns and concepts do fit in large systems.
In the video I've linked above, you can also find Eric explaining that DDD concepts can "break" in very large-scale systems. For instance, imagine a retail system, where each order is an aggregate containing potentially thousands of order items. If you'd like to calculate the order's total amount strictly following DDD, you'd have to load all the order items in memory, which would be extremely inefficient compared leveraging your storage system (e.g. with a clever SQL statement). So, this trade-off should always be kept in mind, DDD is not a silver bullet.
Like I said, maybe I'm just not grasping DDD or maybe I'm over
analyzing my designs?
Excuse me, but I'll have to quote Eric Evans one more time. As he has said, DDD is not for perfectionists, meaning that there might be cases, where the ideal design does not exist and you might have to go with a solution, which is worse in terms of modelling. To read more around that, you can check this article.