From 698b1f6997d88289f2f10f2e2ba6679a54a529dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 22 Jun 2025 19:47:40 +0300 Subject: [PATCH] Revised part-08 --- docs/en/tutorials/modular-crm/part-08.md | 36 ++++++------------------ 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/docs/en/tutorials/modular-crm/part-08.md b/docs/en/tutorials/modular-crm/part-08.md index 8c4de8a857..7133c76a65 100644 --- a/docs/en/tutorials/modular-crm/part-08.md +++ b/docs/en/tutorials/modular-crm/part-08.md @@ -14,11 +14,11 @@ In this part, you will learn how to perform a database-level JOIN operation on t ## The Problem -One essential purpose of modularity is to create modules that hide (encapsulate) their internal data and implementation details from the other modules. These modules communicate with each other through well-defined [integration services](../../framework/api-development/integration-services.md) and [events](framework/infrastructure/event-bus/distributed). In that way, you can independently develop and change module implementations (even modules' database structures) from each other as long as you don't break these inter-module integration points. +One essential purpose of modularity is to create modules that hide (encapsulate) their internal data and implementation details from the other modules. These modules communicate with each other through well-defined [integration services](../../framework/api-development/integration-services.md) and [events](framework/infrastructure/event-bus/distributed). In that way, you can develop and change module implementations (even modules' database structures) independently from each other as long as you don't introduce break changes on these module integration points. In a non-modular application, accessing the related data is easy. You could write a LINQ expression that joins `Orders` and `Products` database tables to get the data with a single database query. It would be easier to implement and execute with a good performance. -On the other hand, it becomes harder to perform operations or get reports requiring access to multiple modules' internal data in a modular system. Remember the *[Implementing Integration Services](part-06.md)* part; We couldn't access the product data inside the Ordering module (`IOrderingDbContext` only defines a `DbSet`), so we needed to create an integration service just to get names of products. This approach is harder to implement and less performant (yet it is acceptable if you don't show too many orders on the UI or properly implement a caching layer). Still, it gives freedom to the Catalog module about its internal database or application logic changes. For example, you can decide to move product data to another physical database or even to another database management system (DBMS) without affecting the other modules. +On the other hand, it becomes harder to perform operations or get reports requiring access to multiple modules' internal data in a modular system. Remember the *[Implementing Integration Services](part-06.md)* part; We couldn't access the product data inside the Ordering module (`IOrderingDbContext` only defines a `DbSet`), so we needed to create an integration service just to get names of products with a list of IDs. This approach is harder to implement and less performant (yet it is acceptable if you don't show too many orders on the UI or properly implement a caching layer). Still, it gives freedom to the Catalog module about its internal database or application logic changes. For example, you can decide to move product data to another physical database or even to another database management system (DBMS) without affecting the other modules. ## A Solution Option @@ -29,31 +29,13 @@ If you want to perform a single database query that spans database tables of mul If these are not problems for you, or if you can handle them when they become problems, you can create reporting modules or aggregator modules that work with multiple modules' data. -In the next section, we will use the main application's codebase to implement such a JOIN operation to keep the tutorial short. However, you already learned how to create new modules, so you can create a new module and develop your JOIN logic inside that new module if you want. +In the next section, we will use the main application's codebase to implement such a JOIN operation to keep the tutorial short. However, you already learned how to create new modules, so you can create a new reporting module and develop your JOIN logic inside that new module if you want. ## The Implementation In this section, we will create an application service in the main application's .NET solution. That application service will perform a LINQ operation on the `Product` and `Order` entities. -### Defining the Reporting Service Interface - -We will define the `IOrderReportingAppService` interface in the main application's .NET solution. - -#### Adding `ModularCrm.Ordering.Contracts` Package Reference - -As the first step, we should reference the `ModularCrm.Ordering.Contracts` package (of the `ModularCrm.Ordering` module) since we will reuse the `OrderState` enum defined in that package. - -Open the ABP Studio's *Solution Explorer* panel, right-click the `ModularCrm` package and select the *Add Package Reference* command: - -![abp-studio-add-package-reference-5](images/abp-studio-add-package-reference-5.png) - -Select the *Imported modules* tab, find and check the `ModularCrm.Ordering.Contracts` package and click the OK button: - -![abp-studio-add-package-reference-dialog-4](images/abp-studio-add-package-reference-dialog-4.png) - -The package reference has been added, and we can now use the types in the `ModularCrm.Ordering.Contracts` package. - -#### Defining the `IOrderReportingAppService` Interface +### Defining the `IOrderReportingAppService` Interface Open the main `ModularCrm` .NET solution in your IDE, create an `Orders` folder under the `Services` folder and add an `IOrderReportingAppService` interface. Here is the definition of that interface: @@ -73,7 +55,7 @@ We have a single method, `GetLatestOrders`, that will return a list of the lates ````csharp using System; -using ModularCrm.Ordering.Contracts.Enums; +using ModularCrm.Ordering; namespace ModularCrm.Orders; @@ -90,7 +72,7 @@ public class OrderReportDto } ```` -`OrderReportDto` contains data from both the `Order` and `Product` entities. We could use the `OrderState` since we have a reference to the package that defines that enum. +`OrderReportDto` contains data from both the `Order` and `Product` entities. After adding these files, the final folder structure should be like this: @@ -104,7 +86,7 @@ Open the `OrderReportingAppService.cs` file and change its content by the follow ````csharp using ModularCrm.Catalog; -using ModularCrm.Ordering.Entities; +using ModularCrm.Ordering; using ModularCrm.Services; using ModularCrm.Services.Orders; using Volo.Abp.Domain.Repositories; @@ -162,11 +144,11 @@ That's all. In that way, you can execute JOIN queries that use data from multipl We haven't created a UI to show list of the latest orders using `OrderReportingAppService`. However, we can use the Swagger UI again to test it. -Open the ABP Studio UI, stop the application if it is running, build and run it again. Once the application starts, browse it, then add `/swagger` to the end of the URL to open the Swagger UI: +Open the ABP Studio UI, stop the application if it is running, build and run it again. Once the application starts, browse it, then add `/swagger` to the end of the URL to open the Swagger UI. Here, find the `OrderReporting` API and execute it as shown below: ![abp-studio-swagger-list-orders](images/abp-studio-swagger-list-orders.png) -Here, find the `OrderReporting` API and execute it as shown above. You should get the order objects with product names. +You should get the order objects with product names. Alternatively, you can visit the `/api/app/order-reporting/latest-orders` URL to directly execute the HTTP API on the browser (you should write the full URL, like `https://localhost:44303/api/app/order-reporting/latest-orders` - port can be different for your case)