Activities of "cangunaydin"

  • ABP Framework version: v5
  • UI type: MVC
  • DB provider: EF Core

Hello, I am new to Abp structure and i was checking out the docs for microservice structure, i have created a sample solution from abp suite. And i was going through the docs and trying to understand how the system works. I believe on the previous version, solution was using internal gateway to do the communication between services. Why was it removed in this version? Actually i am not sure what was the internal gateway was doing on the previous version. was it only a gateway that is publishing the events from rabbitmq and do the communication between all micro-services? And if it was doing all the communication between all microservices before right now how it has been solved? And i believe only "administration service" needs to make a call to "identity service", is there any other microservice making a call to other microservices?

I think all the built-in modules coming from Abp are now using static http client generation explained here: https://github.com/abpframework/abp/blob/dev/docs/en/Blog-Posts/2021-11-18%20v5_0_Preview/POST.md#static-generated-client-proxies-for-c-and-javascript I understand that administration service need to talk to identityservice and this is done with static http client proxies now. But what i don't understand is why administrationservice needs to depend on 5 contractsmodule which are

AbpAccountAdminApplicationContractsModule AbpAccountPublicApplicationContractsModule ProductServiceApplicationContractsModule SaasServiceApplicationContractsModule IdentityServiceApplicationContractsModule

i couldn't see anything on the documentation why these are needed maybe i am missing sth over here maybe it is related with static http client proxies. Maybe these are kind of basic questions but i am new to microservice concept and new abp structure. I would be glad if you could clarify those points for me.

ps: still on the doc it mentions about internal gateway which is not present probably it would be modified when the v5 is released. https://docs.abp.io/en/commercial/5.0/startup-templates/microservice/microservices#administrationservice

Allright i think it is more clear in my mind now, thank you for the answer.

So to summarize, you removed internal gateway because there was only one synched (http) communication which is between administration service and identity service.

All the permissions are defined in application contracts, so this brings us to the conclusion, whatever microservice that implements permission manager module needs to define every microservice application contracts module, so permission manager can do crud operations on the permissions?

And as the last thing if i want to create a communication for ex between productservice and saas service

  • I need to create productservice as a client in identity server give the scope as "saasservice"
  • configure productservice by changing appsettings.json in the project like sth similar that has been made in administration service config.

"AuthServer": { "Authority": "https://localhost:44322", "RequireHttpsMetadata": "true" }, "RemoteServices": { "AbpIdentity": { "BaseUrl": "https://localhost:44388/", "UseCurrentAccessToken": "false" } }, "IdentityClients": { "Default": { "GrantType": "client_credentials", "ClientId": "Adzup_AdministrationService", "ClientSecret": "1q2w3e*", "Authority": "https://localhost:44322", "Scope": "IdentityService" } },

  • And as the last step i need to have the "HttpApiClientModule" of the "saas microservice" in the "productservice module" and i don't need any contracts module since "HttpApiClientModule" also have a reference to the "Saas Contract Module" and this microservice doesn't manage permissions.

Further Questions:

  • So as i understand identityclients options are configured in AbpHttpClientIdentityModelWebModule but in the docs it has been used AbpHttpClientIdentityModelModule can you clarify the difference between 2?

  • Also if i am planning lots of communications between different microservices internally isn't the internal gateway with ocelot is better approach maybe it is not very efficient if one microservice needs to call another but overall can we say that if we have many communcations between our microservices isn't it better to configure an internal gateway instead of configuring all the microservices one by one? What i am asking is are you planning to replace back internal gateway with another solution than ocelot or is it gonna be gone for good?

Thank you so much for the help and assistance

Hello again, I really understand now, also i read this doc from microsoft, which is not a good practice to do synced communications between microservices as they say https://docs.microsoft.com/en-us/dotnet/architecture/microservices/architect-microservice-container-applications/communication-in-microservice-architecture

https://docs.microsoft.com/en-us/dotnet/architecture/microservices/architect-microservice-container-applications/media/communication-in-microservice-architecture/sync-vs-async-patterns-across-microservices.png

So this brings me another question Isn't there anyway to get users without being coupled to identity service. For ex first thing that comes to my mind is to make identityservice not being a seperate microservice and include it in administration service? But probably it is the most used service? Why did you choose to make it a seperate service if it is gonna be coupled to administration service? or how do i need to do decisions when i am seperating monolith project to microservices? Is there any guide or docs or any book to read to learn more about it?

I really appreciate your help, and thank you for answering my questions.

Hello again thank you for the answer.

What makes you think you have to be coupled to identity service for identity user? I would suggest digging more on Domain Driven Design to see how to handle these kind of coupling (using data duplication etc).

Actually i was talking about it from the administration microservice perspective, I was looking at the problem like "reduce the number of intersynched communications if it possible". So that was my intuition at the first place but i understand your point.

There is a great free-ebook Domain Driven Design with the ABP Framework i would suggest. And also Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans.

Thanks for the suggestions i have already read the Domain Driven Design book from Abp, i will check the Eric Evans's Book also.

Thanks for the assistance.

Hello, I have come up to interesting video on youtube from Mauro Servienti. In his presentation he was explaining, about different bounded contexts responsible for different parts of the application. But he used view model composition and decomposition while building the user interface. This makes the solution interesting. Interesting part was each different module wasn't owning the user interface since view was composed from different bounded contexts of the application. https://www.youtube.com/watch?v=KkzvQSuYd5I

So in abp, mostly when you create a bounded context, I automatically think that view should also belong to that module (bounded context). Let me give you an example I was playing around.

I have Product Aggregate which consists of

Product: Aggregate Root

Id: Guid Name: string Description:string StockQuantity:int Price: decimal

also i have Order aggregate which consists of

Order: Aggregate Root

Id:Guid Name: string Items: List<OrderItem> TotalValue:decimal

OrderItem : Entity

Id:Guid OrderId:Guid ProductId:Guid Price: decimal Quantity: decimal Subtotal: decimal

So i wanted to seperate this to two bounded contexts Which is "inventory bounded context" and "sales bounded context". I know that Product in "Sales Bounded Context" and "Inventory Bounded Context" are not the same. In "Sales Bounded Context" i am not interested in how many items are in the stock.

So here the problem arises when i want to create the user interface in "Sales Bounded Context" to create an order. Since I do not know anything about Product, how am i going to list the products here? Two solutions i can think of.

Whenever product is created or updated or deleted on "Inventory bounded context" I will handle that event and create aggregate on "Sales bounded context". So i will copy the product information needed to my "Sales bounded context". Then i can use that in "Sales bounded context".

Second solution is to use IProductAppService in presentation layer of "Sales Bounded Context" which will list the products. This means i need to give the reference to HttpApi.Client project of "Inventory Bounded Context" module. (I don't know what kind of problems does this make? am i coupling my code to the other bounded context?)

Mauro Servienti in the video is giving another solution which says that, presentation layer should be another project which will compose and decompose those bounded contexts which is "inventory bounded context" and "sales bounded context"

So i am pretty confused which way is gonna be the right way. Before I watched the video i was more leaning into copying the products on the "sales bounded context" since it has different properties in that context. Do you have any suggestions how to move forward in these cases? Any advice will be much appreciated. Thank you for reading it.

Hello @liangshiwei As i understand (pls correct me if i am wrong), In microservice solution you don't use the user interface(or web layer) for microservices instead one web app that will unify whole views. And this web app(in EshopOnAbp,it is PublicWebApp) do the requests through gateway (WebPublicGateWay) by using reverse proxy.

So here is the question my project will stay monolith for a while and according to changes it might evolve to microservices in the future, while my modules are monolith where should i implement the user interface to create an order, and if this is going to be inside the module, what should i do to list the products?

Thank you for your patience and for your help a lot.

Of course, the UI layer should be one web app, actually PublicWebApp is it, It just references the UI class library of other modules.

as i understand all the microservice modules do not have any presentation layer which means EshopOnAbp.PublicWeb project do not depend on any microservice module presentation layer (cause there is none, which is not needed on microservice modules in this case). It implements the necessary user interface inside that project. For ex:

  public class ProductDetailModel : AbpPageModel
    {
        [BindProperty(SupportsGet = true)]
        public int OrderNo { get; set; }

        public ProductDto Product { get; private set; }
        public bool IsPurschased { get; private set; }

        private readonly IPublicProductAppService _productAppService;
        private readonly IOrderAppService _orderAppService;

        public ProductDetailModel(
            IPublicProductAppService productAppService,
            IOrderAppService orderAppService)
        {
            _productAppService = productAppService;
            _orderAppService = orderAppService;
        }

        public async Task OnGet(Guid id)
        {
            IsPurschased = (await _orderAppService.GetMyOrdersAsync(new GetMyOrdersInput())).Any(p => p.Items.Any(p => p.ProductId == id));
            Product = await _productAppService.GetAsync(id);
        }
    }

this is a sample from EshopOnAbp, it is ProductDetail page as you can see it is dependent on OrderAppService and ProductAppService while creating the ui. You can do this if you implement the presentation layer out of your modules, like in the demo project EshopOnAbp.

I will try to ask the question from another perspective. If I need to convert the EshopOnAbp project to monolith app, in where should i create the user interface for ProductDetail page?

Cause product belongs to Catalog Module and Order belongs to ordering module. Or you shouldn't implement the ProductDetail page in any modules presentation layer and keep it in your main app?

Hello again, I think we couldn't understand each other :) I know that i can create razor pages. My question is out of scope of what kind of user interface tech. you use.

just let me try another way to explain myself.

Let's think about 3 different bounded contexts.

1- Sales 2- Marketing 3- Shipping

All of them has product properties related to product.

So let's say.

Marketing

 public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    }

Sales

 public class ProductPrice
    {
        public int ProductId { get; set; }

        public decimal Price { get; set; }
    }

Shipping

 public class ProductShippingOptions
    {
        public int Id { get; set; }
        public int ProductId { get; set; }
        public List<ShippingOption> Options { get; set; } = new List<ShippingOption>();
    }

    public class ShippingOption
    {
        public int Id { get; set; }
        public int ProductShippingOptionsId { get; set; }
        public string Option { get; set; }
        public int EstimatedMinDeliveryDays { get; set; }
        public int EstimatedMaxDeliveryDays { get; set; }
    }

so each bounded context is responsible for product details that they care.

So let's think about a moment I am listing products on my web app (it doesn't matter which tech i use). The user wants to see name price and shipping options of the product on a listed view.

If my web app is Monolith App, How should i compose those 3 different services together in a list? If my web app is Microservice, How should i compose those 3 different services together in a list?

3 bounded contexts are different modules and they don't know about each other.

As i see on EshopOnAbp, most of the data is copied between bounded contexts.
here is the code from EshopOnAbp. This is the OrderItem from Ordering Bounded Context.

public class OrderItem : Entity<Guid>
{
    public string ProductCode { get; private set; }
    public string ProductName { get; private set; }
    public string PictureUrl { get; private set; }
    public decimal UnitPrice { get; private set; }
    public decimal Discount { get; private set; }
    public int Units { get; private set; }
    public Guid ProductId { get; private set; }

here is the product from Catalog Bounded Context

 public class Product : AuditedAggregateRoot<Guid>
    {
        /// <summary>
        /// A unique value for this product.
        /// ProductManager ensures the uniqueness of it.
        /// It can not be changed after creation of the product.
        /// </summary>
        [NotNull]
        public string Code { get; private set; }

        [NotNull]
        public string Name { get; private set; }

        public float Price { get; private set; }

        public int StockCount { get; private set; }

        public string ImageName { get; private set; }
        

And in Basket Bounded Context. the app calls the Catalog Bounded Context to get the Product info and caching it.(through _productPublicGrpcClient). This is some kind of composition in microservice level but isn't this should be composed in the gateway instead of Basket Service?

public class BasketItemDto
{
    public Guid ProductId { get; set; }
    public string ProductName { get; set; }
    public string ProductCode { get; set; }
    public string ImageName { get; set; }
    public int Count { get; set; }
    public float TotalPrice { get; set; }
}

is there any other way like viewmodel composition and decomposition that I can use so we don't need to copy the staff around for microservices and i am curious how this composition can happen if i decided to use my modules as monolith?

Hello Galip, Thanks for the answer. I have checked out the "gateway aggregator pattern". And yes this is the thing that i am looking for at the first sight. But there is always but... :) aggregator pattern solves some of my problems for the above example i give. For ex:

If i want to get the details of the product with the id 1. yes aggregator pattern most likely solves my problem. But what about list with search? and what about if i am going to do paging and sorting? and what about multiple column sorting? those are kind of difficult scenarios since data has been shared on different bounded contexts. (pictures are from ViewModel Composition Series of Mauro Servienti, https://milestone.topics.it/2019/02/28/into-the-darkness-of-viewmodel-lists-composition.html)

Also another problem is Decomposition. What i mean by that is when i want to create product from scratch from the nice user interface with all the properties in one page and hit the save button. I need to send every piece of data to the related bounded context. Sth like this. (picture is from the video that i shared, from a ddd conference)

so to manage the things that i have mentioned over here, they have implemented a kind of asp.net core route interceptor which gets the request and calling the parties that is participated in that route, so for ex.

if i do a Get Request on the gateway, sth like api/product/1

then the interceptor will catch this request and look at the contributors (with mediatr pattern) that is registered for this route and call their handle() methods so dto (or viewmodel) can be composed from each contributors.

similarly if i do a Post Request on the gateway, sth like api/product/

then the interceptor will catch this request and look at the contributors (with mediatr pattern) that is registered for this route and call their handle() method but sth is little different this time since you need a transactional boundary they use message broker to do the operation instead of http request.

In conclusion my point is, i have decided to use this concept on abp project, but i am not sure what i am going to gain or loose, so i need to implement and see if it works at the end. So if you have any ideas how this can be done inside abp or any advices about which way should i go, i would like to hear it very much. Thanks for your time and reading it anyway.

Hello again, Thanks for the link, i have watched it now and totally agree on some parts. Also i think if your search needs to be complex you definitely need another microservice for that. But also i have some parts that i disagree.

But before that i am curious about your opinion, how to do the save operations? How you should do that in a microservice architecture if you need to have multiple requests. You can design your pages according to a call that you are gonna do. but that is a kind of limitation from a ui perspective.(Ex: First update name and description, then user will switch to another modal or page to update the price and so on).

The parts that i disagree on the video is, if you hold all the information in a seperate db from different services( i am not saying this is not the way,in some cases you should, for ex,if you wanna do search, paging and sorting together). Basically what you do is kind of cache, and what i believe is in most of the cases, you can do the cache in the gateway instead of another service. And if you do on the gateway level you can give different cache timeout values.

What i mean by that is if marketing is holding the information about name,description. and Sales holding the information about the price. Price information can be cached per day but marketing can be cached for a week so you don't need to call each microservice whenever requests comes(or you can get that information directly from db or text document).

I also started to implement a kind of viewmodel composition gateway on a scratch abp template. I have a question about where the conventional endpoint routes are configured. I want to create an extra endpoint middleware that will intercept the call coming to the gateway and dispatch it to multiple methods and compose the returning values. I have tried to look at the source code but couldn't find where you invoke the method info for the application service endpoints. Can you direct me to the necessary part?

I think in AbpServiceConvention all the services and controllers are converted to endpoints, but i couldn't connect the dots, where and how this class have been used. https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs#L22

Thanks for reading and your input.

Showing 1 to 10 of 91 entries
Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11