Browse Source

Modular crm doc sync

pull/20983/head
ahmetfarukulu 1 year ago
parent
commit
2d332a450b
  1. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-entity-framework-core-migration-dialog.png
  2. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-entity-framework-core-migration.png
  3. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-new-dd-module.png
  4. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-new-ddd-module.png
  5. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-new-empty-module.png
  6. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-new-folder-command.png
  7. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-new-package.png
  8. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-2.png
  9. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-3.png
  10. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-4.png
  11. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-5.png
  12. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-6.png
  13. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-7.png
  14. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-dialog-5.png
  15. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-dialog-6.png
  16. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference.png
  17. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-added-ddd-contracts-package.png
  18. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-build-and-restart-application.png
  19. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-entity-framework-core-add-migration-order.png
  20. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-entity-framework-core-update-database.png
  21. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-graph-build.png
  22. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-import-module-for-ordering.png
  23. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-import-module-ordering.png
  24. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-import-module.png
  25. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-new-folder-dialog.png
  26. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-open-in-explorer.png
  27. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-open-with-visual-studio-main-app.png
  28. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-open-with-visual-studio.png
  29. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-ordering-swagger-ui-in-browser.png
  30. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-solution-runner-graph-build.png
  31. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-solution-runner-initial-product-page.png
  32. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-solution-runner-orders-page.png
  33. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-swagger-ui-create-order-execute.png
  34. BIN
      docs/en/tutorials/modular-crm/images/sql-server-orders-database-table-filled.png
  35. BIN
      docs/en/tutorials/modular-crm/images/sql-server-orders-table-content.png
  36. BIN
      docs/en/tutorials/modular-crm/images/visual-studio-ordering-contracts.png
  37. BIN
      docs/en/tutorials/modular-crm/images/visual-studio-ordering-controller.png
  38. 2
      docs/en/tutorials/modular-crm/part-02.md
  39. 236
      docs/en/tutorials/modular-crm/part-05.md
  40. 128
      docs/en/tutorials/modular-crm/part-06.md
  41. 118
      docs/en/tutorials/modular-crm/part-07.md

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-entity-framework-core-migration-dialog.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-entity-framework-core-migration.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 70 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-new-dd-module.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-new-ddd-module.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-new-empty-module.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-new-folder-command.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-new-package.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 31 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-3.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-4.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-5.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-dialog-5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference-dialog-6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-add-package-reference.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-added-ddd-contracts-package.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-build-and-restart-application.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-entity-framework-core-add-migration-order.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-entity-framework-core-update-database.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 91 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-graph-build.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 36 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-import-module-for-ordering.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 21 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-import-module-ordering.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-import-module.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-new-folder-dialog.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-open-in-explorer.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 67 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-open-with-visual-studio-main-app.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 74 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-open-with-visual-studio.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-ordering-swagger-ui-in-browser.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-solution-runner-graph-build.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-solution-runner-initial-product-page.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 79 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-solution-runner-orders-page.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 56 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-swagger-ui-create-order-execute.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/en/tutorials/modular-crm/images/sql-server-orders-database-table-filled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
docs/en/tutorials/modular-crm/images/sql-server-orders-table-content.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

BIN
docs/en/tutorials/modular-crm/images/visual-studio-ordering-contracts.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
docs/en/tutorials/modular-crm/images/visual-studio-ordering-controller.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

2
docs/en/tutorials/modular-crm/part-02.md

@ -41,7 +41,7 @@ We will use the *DDD Module* template for the Product module and the *Empty Modu
Right-click the `modules` folder on the *Solution Explorer* panel, and select the *Add* -> *New Module* -> *DDD Module* command:
![abp-studio-add-new-dd-module](images/abp-studio-add-new-dd-module.png)
![abp-studio-add-new-ddd-module](images/abp-studio-add-new-ddd-module.png)
This command opens a new dialog to define the properties of the new module. You can use the following values to create a new module named `ModularCrm.Products`:

236
docs/en/tutorials/modular-crm/part-05.md

@ -232,9 +232,215 @@ After the operation completes, you can check your database to see the new `Order
![sql-server-products-database-table](images/sql-server-orders-database-table.png)
## Creating the User Interface
## Creating the Application Service
We will create an application service to manage the `Order` entities.
### Defining the Application Service Contract
We're gonna create the `IOrderAppService` interface under the `ModularCrm.Ordering.Contracts` project but first, we need to add `Volo.Abp.Ddd.Application.Contracts` package reference.
Right-click the `ModularCrm.Ordering.Contracts` project in the *Solution Explorer* panel and select the *Add Package Reference* command:
![abp-studio-add-package-reference-6](images/abp-studio-add-package-reference-6.png)
This command opens a dialog to add a new package reference:
![abp-studio-add-package-reference-dialog-5](images/abp-studio-add-package-reference-dialog-5.png)
Select the *NuGet* tab, type `Volo.Abp.Ddd.Application.Contracts` as the *Package name* and write the version of the package you want to install. Please be sure that you are installing the same version as the other ABP packages you are already using.
Click the *Ok* button. Now you can check the *Packages* under the `ModularCrm.Ordering.Contracts` project *Dependencies* to see the `Volo.Abp.Ddd.Application.Contracts` package is installed:
![abp-studio-added-ddd-contracts-package](images/abp-studio-added-ddd-contracts-package.png)
Return to your IDE, open the `ModularCrm.Ordering` module's .NET solution and create an `IOrderAppService` interface under the `Services` folder for `ModularCrm.Ordering.Contracts` project:
````csharp
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace ModularCrm.Ordering.Contracts.Services;
public interface IOrderAppService : IApplicationService
{
Task<List<OrderDto>> GetListAsync();
Task CreateAsync(OrderCreationDto input);
}
````
### Defining Data Transfer Objects
The `GetListAsync` and `CreateAsync` methods will use data transfer objects (DTOs) to communicate with the client. We will create two DTO classes for that purpose.
Create a `OrderCreationDto` class under the `ModularCrm.Ordering.Contracts` project:
````csharp
using System;
using System.ComponentModel.DataAnnotations;
namespace ModularCrm.Ordering.Contracts.Services;
public class OrderCreationDto
{
[Required]
[StringLength(150)]
public string CustomerName { get; set; }
[Required]
public Guid ProductId { get; set; }
}
````
Create a `OrderDto` class under the `ModularCrm.Ordering.Contracts` project:
````csharp
using System;
using ModularCrm.Ordering.Contracts.Enums;
namespace ModularCrm.Ordering.Contracts.Services;
public class OrderDto
{
public Guid Id { get; set; }
public string CustomerName { get; set; }
public Guid ProductId { get; set; }
public OrderState State { get; set; }
}
````
The new files under the `ModularCrm.Ordering.Contracts` project should be like the following figure:
![visual-studio-ordering-contracts](images/visual-studio-ordering-contracts.png)
### Implementing the Application Service
Before creating the `OrderAppService` class, we need to add the `Volo.Abp.Ddd.Application` and `Volo.Abp.AutoMapper` packages to the Ordering module.
Right-click the `ModularCrm.Ordering` package in the *Solution Explorer* panel and select the *Add Package Reference* command:
![abp-studio-add-package-reference-7](images/abp-studio-add-package-reference-7.png)
This command opens a dialog to add a new package reference:
![abp-studio-add-package-reference-dialog-6](images/abp-studio-add-package-reference-dialog-6.png)
Select the *NuGet* tab, enter `Volo.Abp.Ddd.Application` as the *Package name*, and specify the version of the package you wish to install. Afterward, you can add the `Volo.Abp.AutoMapper` package in the same dialog. Ensure that you install the same version as the other ABP packages you are already using.
Click the *OK* button. Now we should configure the *AutoMapper* object to map the `Order` entity to the `OrderDto` object. We will create a class named `OrderingApplicationAutoMapperProfile` under the `ModularCrm.Ordering` project:
````csharp
using AutoMapper;
using ModularCrm.Ordering.Contracts.Services;
using ModularCrm.Ordering.Entities;
namespace ModularCrm.Ordering;
public class OrderingApplicationAutoMapperProfile : Profile
{
public OrderingApplicationAutoMapperProfile()
{
CreateMap<Order, OrderDto>();
}
}
````
Since this is a non-layered module, we can use entities and repositories directly on the user interface. If you think that is not a good practice, then use the layered module template as we've already done for the *Products* module. But for the Ordering module, we will keep it very simple for this tutorial to show it is also possible.
And configure the `OrderingWebModule` class to use the `OrderingApplicationAutoMapperProfile`:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
//Add these lines
context.Services.AddAutoMapperObjectMapper<OrderingWebModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<OrderingWebModule>(validate: true);
});
}
````
Now, we can implement the `IOrderAppService` interface. Create an `OrderAppService` class under the `Services` folder of the `ModularCrm.Ordering` project:
````csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ModularCrm.Ordering.Contracts.Enums;
using ModularCrm.Ordering.Contracts.Services;
using ModularCrm.Ordering.Entities;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace ModularCrm.Ordering.Services;
public class OrderAppService : ApplicationService, IOrderAppService
{
private readonly IRepository<Order> _orderRepository;
public OrderAppService(IRepository<Order, Guid> orderRepository)
{
_orderRepository = orderRepository;
ObjectMapperContext = typeof(OrderingWebModule);
}
public async Task<List<OrderDto>> GetListAsync()
{
var orders = await _orderRepository.GetListAsync();
return ObjectMapper.Map<List<Order>, List<OrderDto>>(orders);
}
public async Task CreateAsync(OrderCreationDto input)
{
var order = new Order
{
CustomerName = input.CustomerName,
ProductId = input.ProductId,
State = OrderState.Placed
};
await _orderRepository.InsertAsync(order);
}
}
````
Open the `ModularCrmWebModule` class in the main application's solution (the `ModularCrm` solution), find the `ConfigureAutoApiControllers` method and add the following lines inside that method:
````csharp
private void ConfigureAutoApiControllers()
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(ModularCrmApplicationModule).Assembly);
options.ConventionalControllers.Create(typeof(ProductsApplicationModule).Assembly);
//ADD THE FOLLOWING LINE:
options.ConventionalControllers.Create(typeof(OrderingWebModule).Assembly);
});
}
````
### Creating Example Orders
This section will create a few example orders using the [Swagger UI](../../framework/api-development/swagger.md). Thus, we will have some sample orders to show on the UI.
Now, right-click the `ModularCrm` under the `main` folder in the Solution Explorer panel and select the *Dotnet CLI* -> *Graph Build* command. This will ensure that the order module and the main application are built and ready to run.
After the build process completes, open the Solution Runner panel and click the *Play* button near the solution root. Once the `ModularCrm.Web` application runs, we can right-click it and select the *Browse* command to open the user interface.
Once you see the user interface of the web application, type `/swagger` at the end of the URL to open the Swagger UI. If you scroll down, you should see the `Orders` API:
![abp-studio-ordering-swagger-ui-in-browser](images/abp-studio-ordering-swagger-ui-in-browser.png)
Expand the `/api/app/order` API and click the *Try it out* button. Then, create a few orders by filling in the request body and clicking the *Execute* button:
![abp-studio-swagger-ui-create-order-execute](images/abp-studio-swagger-ui-create-order-execute.png)
If you check the database, you should see the entities created in the *Orders* table:
![sql-server-orders-database-table-filled](images/sql-server-orders-database-table-filled.png)
## Creating the User Interface
### Creating a `_ViewImports.cshtml` File
@ -257,28 +463,26 @@ Create an `Orders` folder under the `Pages` folder and add an `Index.cshtml` Raz
````csharp
using Microsoft.AspNetCore.Mvc.RazorPages;
using ModularCrm.Ordering.Entities;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using ModularCrm.Ordering.Contracts.Services;
namespace ModularCrm.Ordering.Pages.Orders
{
public class IndexModel : PageModel
{
public List<Order> Orders { get; set; }
public List<OrderDto> Orders { get; set; }
private readonly IRepository<Order, Guid> _orderRepository;
private readonly IOrderAppService _orderAppService;
public IndexModel(IRepository<Order, Guid> orderRepository)
public IndexModel(IOrderAppService orderAppService)
{
_orderRepository = orderRepository;
_orderAppService = orderAppService;
}
public async Task OnGetAsync()
{
Orders = await _orderRepository.GetListAsync();
Orders = await _orderAppService.GetListAsync();
}
}
}
@ -310,14 +514,6 @@ Here, we are injecting a repository to query `Order` entities from the database
This page shows a list of orders on the UI. We haven't created a UI to create new orders, and we will not do it to keep this tutorial simple. If you want to learn how to create advanced UIs with ABP, please follow the [Book Store tutorial](../book-store/index.md).
### Creating Some Sample Data
You can open the database and manually create a few order records to show on the UI:
![sql-server-orders-table-content](images/sql-server-orders-table-content.png)
You can get `ProductId` values from the `Products` table and [generate](https://www.guidgenerator.com/) some random GUIDs for other GUID fields.
### Building the Application
Now, we will run the application to see the result. Please stop the application if it is already running. Then open the *Solution Runner* panel, right-click the `ModularCrm.Web` application, and select the *Build* -> *Graph Build* command:
@ -375,11 +571,13 @@ namespace ModularCrm.Ordering
`OrderingMenuContributor` implements the `IMenuContributor` interface, which forces us to implement the `ConfigureMenuAsync` method. In that method, we can manipulate the menu items (add new menu items, remove existing menu items or change the properties of existing menu items). The `ConfigureMenuAsync` method is executed whenever the menu is rendered on the UI, so you can dynamically decide how to manipulate the menu items.
After creating such a class, we should configure the `AbpNavigationOptions` to add that contributor. Open the `OrderingWebModule` class in the `ModularCrm.Ordering` project and add the following configuration code into the `ConfigureServices` method (if there is no `ConfigureServices` method, first create it as shown below):
After creating such a class, we should configure the `AbpNavigationOptions` to add that contributor. Open the `OrderingWebModule` class in the `ModularCrm.Ordering` project and add the following configuration code into the `ConfigureServices` method:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
//... other configurations
Configure<AbpNavigationOptions>(options =>
{
options.MenuContributors.Add(new OrderingMenuContributor());

128
docs/en/tutorials/modular-crm/part-06.md

@ -145,68 +145,121 @@ ABP Studio adds the package reference and arranges the [module](../../framework/
Now, we can inject and use `IProductIntegrationService` in the Ordering module codebase.
Open the `IndexModel` class (the `IndexModel.cshtml.cs` file under the `Pages/Orders` folder of the `ModularCrm.Ordering` project of the `ModularCrm.Ordering` .NET solution) and change its content as like the following code block:
Open the `OrderAppService` class (the `OrderAppService.cs` file under the `Services` folder of the `ModularCrm.Ordering` project of the `ModularCrm.Ordering` .NET solution) and change its content as like the following code block:
````csharp
using Microsoft.AspNetCore.Mvc.RazorPages;
using ModularCrm.Ordering.Entities;
using ModularCrm.Products.Integration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ModularCrm.Ordering.Contracts.Enums;
using ModularCrm.Ordering.Contracts.Services;
using ModularCrm.Ordering.Entities;
using ModularCrm.Products.Integration;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace ModularCrm.Ordering.Pages.Orders
namespace ModularCrm.Ordering.Services;
public class OrderAppService : ApplicationService, IOrderAppService
{
public class IndexModel : PageModel
private readonly IRepository<Order> _orderRepository;
private readonly IProductIntegrationService _productIntegrationService;
public OrderAppService(
IRepository<Order, Guid> orderRepository,
IProductIntegrationService productIntegrationService)
{
public List<Order> Orders { get; set; }
// Define a dictionary for Id -> Name conversion
public Dictionary<Guid, string> ProductNames { get; set; }
_orderRepository = orderRepository;
_productIntegrationService = productIntegrationService;
ObjectMapperContext = typeof(OrderingWebModule);
}
private readonly IRepository<Order, Guid> _orderRepository;
private readonly IProductIntegrationService _productIntegrationService;
public async Task<List<OrderDto>> GetListAsync()
{
var orders = await _orderRepository.GetListAsync();
public IndexModel(
IRepository<Order, Guid> orderRepository,
IProductIntegrationService productIntegrationService)
// Prepare a list of products we need
var productIds = orders.Select(o => o.ProductId).Distinct().ToList();
var products = (await _productIntegrationService
.GetProductsByIdsAsync(productIds))
.ToDictionary(p => p.Id, p => p.Name);
var result = ObjectMapper.Map<List<Order>, List<OrderDto>>(orders);
result = result.Select(a =>
{
_orderRepository = orderRepository;
_productIntegrationService = productIntegrationService;
}
a.ProductName = products[a.ProductId];
return a;
})
.ToList();
return result;
}
public async Task OnGetAsync()
public async Task CreateAsync(OrderCreationDto input)
{
var order = new Order
{
// Getting the orders from this module's database
Orders = await _orderRepository.GetListAsync();
CustomerName = input.CustomerName,
ProductId = input.ProductId,
State = OrderState.Placed
};
await _orderRepository.InsertAsync(order);
}
}
````
// Prepare a list of products we need
var productIds = Orders.Select(o => o.ProductId).Distinct().ToList();
And also, open the `OrderDto` class (the `OrderDto.cs` file under the `Services` folder of the `ModularCrm.Ordering.Contracts` project of the `ModularCrm.Ordering` .NET solution) and add a `ProductName` property to it:
// Request the related products from the product integration service
var products = await _productIntegrationService
.GetProductsByIdsAsync(productIds);
````csharp
using System;
using ModularCrm.Ordering.Contracts.Enums;
// Create a dictionary to get a product name easily by its id
ProductNames = products.ToDictionary(p => p.Id, p => p.Name);
}
namespace ModularCrm.Ordering.Contracts.Services;
public class OrderDto
{
public Guid Id { get; set; }
public string CustomerName { get; set; }
public Guid ProductId { get; set; }
public string ProductName { get; set; } // New property
public OrderState State { get; set; }
}
````
Lastly, open the `OrderingApplicationAutoMapperProfile` class (the `OrderingApplicationAutoMapperProfile.cs` file under the `Services` folder of the `ModularCrm.Ordering` project of the `ModularCrm.Ordering` .NET solution) and ignore the `ProductName` property in the mapping configuration:
````csharp
using AutoMapper;
using ModularCrm.Ordering.Contracts.Services;
using ModularCrm.Ordering.Entities;
using Volo.Abp.AutoMapper;
namespace ModularCrm.Ordering;
public class OrderingApplicationAutoMapperProfile : Profile
{
public OrderingApplicationAutoMapperProfile()
{
CreateMap<Order, OrderDto>()
.Ignore(x => x.ProductName); // New line
}
}
````
Let's see what we've changed:
* We have defined a `ProductNames` dictionary. We will use it on the UI to convert product IDs to product names. We are filling that dictionary with products from the product integration service.
* We've added a `ProductName` property to the `OrderDto` class to store the product name.
* Injecting the `IProductIntegrationService` interface so we can use it to request products.
* In the `OnGetAsync` method;
* In the `GetListAsync` method;
* First getting the orders from the ordering module's database just like done before.
* Next, we are preparing a unique list of product IDs since the `GetProductsByIdsAsync` method requests it.
* Then we are calling the `IProductIntegrationService.GetProductsByIdsAsync` method to get a `List<ProductDto>` object.
* In the last line, we are converting the product list to a dictionary, where the key is `Guid Id` and the value is `string Name`. That way, we can easily find a product's name with its ID.
* Finally, we are mapping the orders to `OrderDto` objects and setting the product name by looking up the product ID in the dictionary.
Open the `Index.cshtml` file, and change the `@order.ProductId` part by `@Model.ProductNames[order.ProductId]` to write the product name instead of the product ID. The final `Index.cshtml` content should be the following:
Open the `Index.cshtml` file, and change the `@order.ProductId` part by `@Model.ProductName` to write the product name instead of the product ID. The final `Index.cshtml` content should be the following:
````html
@page
@ -219,11 +272,11 @@ Open the `Index.cshtml` file, and change the `@order.ProductId` part by `@Model.
<abp-list-group>
@foreach (var order in Model.Orders)
{
<abp-list-group-item>
<strong>Customer:</strong> @order.CustomerName <br />
<strong>Product:</strong> @Model.ProductNames[order.ProductId] <br />
<strong>State:</strong> @order.State
</abp-list-group-item>
<abp-list-group-item>
<strong>Customer:</strong> @order.CustomerName <br />
<strong>Product:</strong> @order.ProductName <br />
<strong>State:</strong> @order.State
</abp-list-group-item>
}
</abp-list-group>
</abp-card-body>
@ -241,4 +294,3 @@ In the way explained in this section, you can easily create integration services
> **Design Tip**
>
> It is suggested that you keep that type of communication to a minimum and not couple your modules with each other. It can make your solution complicated and may also decrease your system performance. When you need to do it, think about performance and try to make some optimizations. For example, if the Ordering module frequently needs product data, you can use a kind of [cache layer](../../framework/fundamentals/caching.md), so it doesn't make frequent requests to the Products module. Especially if you consider converting your system to a microservice solution in the future, too many direct integration API calls can be a performance bottleneck.

118
docs/en/tutorials/modular-crm/part-07.md

@ -56,83 +56,87 @@ namespace ModularCrm.Ordering.Contracts.Events
### Using the `IDistributedEventBus` Service
The `IDistributedEventBus` service publishes events to the event bus. Until this point, the Ordering module has no functionality to create new orders.
In Part 3, we used ABP's Auto HTTP API Controller feature to expose HTTP APIs from application services automatically. In this section, we will create an ASP.NET Core API controller class to create a new order. In that way, you will also see that it is not different from creating a regular ASP.NET Core controller.
Open the `ModularCrm.Ordering` module's .NET solution, create a `Controllers` folder in the `ModularCrm.Ordering` project and place a controller class named `OrdersController` in that new folder. The final folder structure should be like that:
![visual-studio-ordering-controller](images/visual-studio-ordering-controller.png)
Here is the full `OrdersController` class:
The `IDistributedEventBus` service publishes events to the event bus. Until this point, the Ordering module has no functionality to create new orders. Let's change that and place an order, for that purpose open the `ModularCrm.Ordering` module's .NET solution, and update the `OrderAppService` as follows:
````csharp
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ModularCrm.Ordering.Contracts.Enums;
using ModularCrm.Ordering.Contracts.Events;
using ModularCrm.Ordering.Contracts.Services;
using ModularCrm.Ordering.Entities;
using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc;
using ModularCrm.Products.Integration;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus.Distributed;
namespace ModularCrm.Ordering.Controllers
namespace ModularCrm.Ordering.Services;
public class OrderAppService : ApplicationService, IOrderAppService
{
[Route("api/orders")]
[ApiController]
public class OrdersController : AbpControllerBase
private readonly IRepository<Order> _orderRepository;
private readonly IProductIntegrationService _productIntegrationService;
private readonly IDistributedEventBus _distributedEventBus;
public OrderAppService(
IRepository<Order, Guid> orderRepository,
IProductIntegrationService productIntegrationService,
IDistributedEventBus distributedEventBus)
{
private readonly IRepository<Order, Guid> _orderRepository;
private readonly IDistributedEventBus _distributedEventBus;
_orderRepository = orderRepository;
_productIntegrationService = productIntegrationService;
_distributedEventBus = distributedEventBus;
ObjectMapperContext = typeof(OrderingWebModule);
}
public OrdersController(
IRepository<Order, Guid> orderRepository,
IDistributedEventBus distributedEventBus)
{
_orderRepository = orderRepository;
_distributedEventBus = distributedEventBus;
}
public async Task<List<OrderDto>> GetListAsync()
{
var orders = await _orderRepository.GetListAsync();
// Prepare a list of products we need
var productIds = orders.Select(o => o.ProductId).Distinct().ToList();
var products = (await _productIntegrationService
.GetProductsByIdsAsync(productIds))
.ToDictionary(p => p.Id, p => p.Name);
[HttpPost]
public async Task<IActionResult> CreateAsync(OrderCreationModel input)
var result = ObjectMapper.Map<List<Order>, List<OrderDto>>(orders);
result = result.Select(a =>
{
// Create a new Order entity
var order = new Order
{
CustomerName = input.CustomerName,
ProductId = input.ProductId,
State = OrderState.Placed
};
// Save it to the database
await _orderRepository.InsertAsync(order);
// Publish an event so other modules can be informed
await _distributedEventBus.PublishAsync(
new OrderPlacedEto
{
ProductId = order.ProductId,
CustomerName = order.CustomerName
});
return Created();
}
a.ProductName = products[a.ProductId];
return a;
})
.ToList();
public class OrderCreationModel
return result;
}
public async Task CreateAsync(OrderCreationDto input)
{
// Create a new Order entity
var order = new Order
{
public Guid ProductId { get; set; }
CustomerName = input.CustomerName,
ProductId = input.ProductId,
State = OrderState.Placed
};
[Required]
[StringLength(120)]
public string CustomerName { get; set; }
}
// Save it to the database
await _orderRepository.InsertAsync(order);
// Publish an event so other modules can be informed
await _distributedEventBus.PublishAsync(
new OrderPlacedEto
{
ProductId = order.ProductId,
CustomerName = order.CustomerName
});
}
}
````
The `OrdersController.CreateAsync` method creates a new `Order` entity, saves it to the database and finally publishes an `OrderPlacedEto` event.
The `OrderAppService.CreateAsync` method creates a new `Order` entity, saves it to the database and finally publishes an `OrderPlacedEto` event.
## Subscribing to an Event

Loading…
Cancel
Save