Eshoponcontainers: [Question] about organization of code

Created on 22 Jul 2018  路  9Comments  路  Source: dotnet-architecture/eShopOnContainers

This is more a question that I would like to get some clarification.

I would first like to iterate that I know this is demo code and I have no issues with the code itself.

According to "Architecting Modern Web Apps with ASP.NET Core 2 and Azure" and "NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook)", there is mention of separating business logic into its own components.

I noticed (or cannot find) folders or components specifically calling out it is business logic so I just want to make sure if this is something that is just documented and not implemented or I am just way off.

From what I thought, wouldn't CatalogController.cs logic within the endpoints be moved into said business logic component?

I do recognize that in the case of the demo, if it passes EF Validation, most cases are fine and that basically handles the use for BL but still seems odd.

I would imagine something along these lines:

  • CatalogController.cs uses DI on the Business Logic Component (ICatalogBL)
  • ICatalogBL contains the contract for the implementation BusinessLogic
  • CatalogBL implements ICatalogBL and DI on the services
  • Service calls are handled in CatalogBL and CatalogController.cs becomes essentially calling the BL and return results based off if success or not (or whatever is required).

Final question is related to ViewModels and the constructors: I am sure this is beyond the scope of this demo, but why not use something like AutoMapper? Just seems like that would be a much better option (especially when modifying models and view models) for maintainability?

Again, just want clarification and love that you all (MS and contributors) are giving great examples!

question

Most helpful comment

@Zenoware - Good question. As specified in the book and in the home page of eShopOnContainers, this solution shows multiple approaches for microservices, from simple CRUD microservices to more elaborated Domain-Driven Design microservices.
The Catalog microservice is a pretty simple CRUD microservice so everything is implemented right away in the controllers. It'd be the simplest approach.

If you'd like to research a more elaborated microservice which has the transactional area segregated from the controllers, that would be the Ordering microservice which follows a simplified CQRS, Commands and many more DDD patterns (Domain Events, Aggregates, etc.), described in the book.

AutoMapper is great when you need to convert from original entities to DTOs, but usually, in our microservices we're directly generating the ViewModels directly from the database which offers a better performance than converting to a new structure through AutoMapper going through all the data to be converted, in-memory.

In any case, if you have a good scenario for AutoMapper, go for it. AutoMapper is a good approach for many scenarios.

All 9 comments

@Zenoware - Good question. As specified in the book and in the home page of eShopOnContainers, this solution shows multiple approaches for microservices, from simple CRUD microservices to more elaborated Domain-Driven Design microservices.
The Catalog microservice is a pretty simple CRUD microservice so everything is implemented right away in the controllers. It'd be the simplest approach.

If you'd like to research a more elaborated microservice which has the transactional area segregated from the controllers, that would be the Ordering microservice which follows a simplified CQRS, Commands and many more DDD patterns (Domain Events, Aggregates, etc.), described in the book.

AutoMapper is great when you need to convert from original entities to DTOs, but usually, in our microservices we're directly generating the ViewModels directly from the database which offers a better performance than converting to a new structure through AutoMapper going through all the data to be converted, in-memory.

In any case, if you have a good scenario for AutoMapper, go for it. AutoMapper is a good approach for many scenarios.

@CESARDELATORRE I get that the "simplest approach" was implemented here, but should one not instead encourage best practices by leading by example, regardless of the simplicity of the project? I mean, I've seen way too many projects that started off with the "simplest approach" just because there was no immediate need for additional abstraction, and then they quickly outgrow that justification and find themselves in trouble, with flawed, copy-pasted operations in areas (e.g., controllers) that, instead, could have been reduced to a single point of failure (e.g., common business logic).

And there are just as many if not more projects that introduce more complexity than necessary at the beginning and then never manage to ship working software. I think showing both simple as well as more complex and robust options is a good mix for this sample, provided the options are laid out clearly for developers so they can make sound choices about which approach they need in their own applications.

Separating business logic into a business logic layer does not add unnecessary complexity. It is very common practice and allows all the real logic to be kept in one place; I would say that makes for less complexity.

Controllers should be dumb. They should call business logic and return a result; nothing more. By separating business logic into a business logic layer from the start, your application becomes more extensible and much more maintainable. I can't see the argument that a business logic layer counts as "more complexity than necessary" given how standard and simple it is to do.

Everything depends on what you are doing/implementing.
A simple data-driven service, like the Catalog service, which is just CRUD (create/read/update/delete) service has almost no business logic, so it doesn't make sense to add complexity there.
The Ordering microservice is the one where we have more business logic, that's why we're using Domain-Driven Design patterns in there.
Over engineering is also dangerous.
I've written multiple books about Domain-Driven Design and microservices and I can tell you that I've seen people applying complex patterns into "everything", and those patterns should only be aplied to your Core Domain. Over-complicating apps is not good.
Creating multiple layers just because you can, is not good, neither.

If you have something very simple, keep it simple.
If you have complexity, a lot of business logic, then tackle it with advanced patterns, decouple it, use
DDD patterns, Aggregates and aggregate-roots, etc. but do not over-engineer a very simple data-driven CRUD service which might not have business rules/logic.

Just database access is not "business logic".

Finally, in a microservices world, it is good to show multiple different types of microservices. Simple to get started (i.e. the Catalog microservice) and more advanced (like the Ordering microservice), when you need to tackle complexity and significant business logic.

@CESARDELATORRE thanks for the response and I will definitely take a look at the orders side to check out! @ardalis I am not getting your argument on added complexity. Maybe I am thinking design complexity but I have to agree with @IRCraziestTaxi on that argument. I will also admit that if you are doing a simple DB operation (like the case of the example I was originally looking at), that does add unnecessary abstraction. But I personally would prefer an abstracted response from controllers to keep a standard. That goes beyond separating out via business logic. I do like the idea of MediatR tho!

@CESARDELATORRE one other quick question and I understand why it is done the way it is because Orders is the only "entity" to implement MediatR/Aggregates, but, let's say we are using aggregates among other entities, a ton of this stuff could be abstracted right? I imagine the SeedWork and Exceptions could be (and mostly already is) generic. I guess the fact that Order is only implementing Aggregates is why I am a bit confused because those two namespaces exist only in the Order.Domain library. Sorry for the flood of questions, just want to make sure I am wrapping my head around this because I think this is some pretty amazing stuff!

Hi @Zenoware,

I kind of like too the Mediator pattern as a general practice, independently of using aggregates or any other higher level pattern but haven't used it in a real project yet.

I have a small experimental (trivial/CRUD) project to test drive the concept, though only on the backend for now, you might want to take a look: https://github.com/mvelosop/Microflow-X.

Just run the (SpecFlow based) tests.

Hope this helps a bit.

Closing this issue now, will reopen if needed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rafsanulhasan picture rafsanulhasan  路  3Comments

Diggzinc picture Diggzinc  路  5Comments

arindams621 picture arindams621  路  5Comments

Dudelsb picture Dudelsb  路  3Comments

CESARDELATORRE picture CESARDELATORRE  路  3Comments