Humanizer

In this tutorial we will create a customer microservice and use the Humanizer library to record the registration date and also define a string field with the date in ordinal format.

Important

The following example uses the Humanizer library, but you can use any library you want, the focus at this point is on understanding how Devprime enables the use of extensions and how to integrate them into your project without hurting the established architecture principles, ensuring separation of responsibilities and decoupling.

Creating a microservice to use in the example

We will use a microservice based on the Devprime platform

Type the following command in a directory of your choice, and then open the project in Visual Studio, VSCODE, or your preferred IDE:
dp new customer --stream rabbitmq --state mongodb

Go to the root folder of the project: .\customer\

Domain Implementations

We need to include a aggregate root to compose the business features of our context, so type the following command in your terminal:
dp add aggregate Customer

The CLI should create the Customer.cs class where we will include our first business rules. Open the path: code .\src\core\domain\aggregates\customer\Customer.cs and include the properties as shown in the example below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
namespace Domain.Aggregates.Customer;
public class Customer : AggRoot
{
    public string FullName { get; private set; }
    public string CustomerTaxID { get; private set; }
    public string Address { get; private set; }
    public string Phone { get; private set; }
    public string Email { get; private set; }
    public DateTime CreationDate { get; private set; }
    public string OrdinalCreationDate { get; private set; }

    public void SetOrdinalDate(string ordinalCreationDate)
    {
        OrdinalCreationDate = ordinalCreationDate;
    }
}

Speeding up implementations with the CLI

The next step will be to use the CLI to speed up most of the necessary implementations and for that type the command below in your terminal:

dp init

The CLI asks for authorization to change the necessary files of the solution, in this case we will type A to authorize all files.

Re-access aggregate root at the path: code .\src\core\domain\aggregates\customer\Customer.cs and change the Add() method as in the following example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public virtual void Add()
{
    Dp.Pipeline(Execute: () =>
    {
        ID = Guid.NewGuid();
        IsNew = true;
        CreationDate = DateTime.UtcNow;

        ValidFields();
        Dp.ProcessEvent(new CustomerCreated());
    });
}

Still in aggregate root change the ValidFields() method by removing the validation for the field OrdinalCreationDate as shown in the following example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private void ValidFields()
{
    if (String.IsNullOrWhiteSpace(FullName))
        Dp.Notifications.Add("FullName is required");
    if (String.IsNullOrWhiteSpace(CustomerTaxID))
        Dp.Notifications.Add("CustomerTaxID is required");
    if (String.IsNullOrWhiteSpace(Address))
        Dp.Notifications.Add("Address is required");
    if (String.IsNullOrWhiteSpace(Phone))
        Dp.Notifications.Add("Phone is required");
    if (String.IsNullOrWhiteSpace(Email))
        Dp.Notifications.Add("Email is required");
    if (CreationDate == DateTime.MinValue)
        Dp.Notifications.Add("CreationDate is required");

    Dp.Notifications.ValidateAndThrow();
}

Adding an extension

We will now include the HumanService class that will be our extension, via the following Devprime CLI command.

dp add extensions HumanService

The CLI requests authorization to change the required files from the solution, press A to authorize all files.

The following image shows the HumanService class and its Interface IHumanService, which will be responsible for implementing the integrations with the Humanizer library.

It also shows the class Extensions and the interface IExtensions whose purpose is to be a proxy that allows the Devprime Pipeline execution context to access all the extensions available in the application via dependency injection.

Devprime How To Extensions Key `

Adicionando a referência para a biblioteca Humanizer

Nós vamos incluir a biblioteca Humanizer em nosso projeto, lembre-se de definir o destino de instalação para o projeto “Devprime.Extensions”. code .\src\adapters\extensions\Devprime.Extensions.csproj

Via dotnet CLI

dotnet add .\src\adapters\extensions\Devprime.Extensions.csproj package Humanizer

Rode também o dotnet build no projeto. (Lembre-se de estar na pasta raíz do projeto .\customer\):

dotnet build

Implementando a integração com a biblioteca Humanizer

Nós vamos alterar a interface “IHumanService” existente no caminho code .\src\core\application\interfaces\adapters\extensions\IHumanService.cs, incluindo a assinatura de método a seguir:

1
string ToOrdinalWords(DateTime date);

Nós também precisamos implementar o método na classe “HumanService” code .\src\adapters\extensions\humanservice\HumanService.cs, deixando-a conforme segue:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
using Humanizer;
using System;
namespace Devprime.Extensions.HumanService;
public class HumanService : DevprimeExtensions, IHumanService
{
    public HumanService(IDpExtensions dp) : base(dp)
    {
    }

    public string ToOrdinalWords(DateTime date)
    {
        return Dp.Pipeline(ExecuteResult: () =>
        {
            var result = date.ToOrdinalWords();
            return result;
        });
    }
}

Implementado a chamada ao adapter de extension

O nosso cenário envolve um evento de domínio chamado “CustomerCreated” e associado a ele temos um EventHandler chamado “CustomerCreatedEventHandler”, que possui uma chamada ao Devprime State Adapter para persistência dos dados (linha 11) e emite um evento externo à aplicação (linha 26).
Caminho: code .\src\Core\Application\EventHandlers\Customer\CustomerCreatedEventHandler.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
namespace Application.EventHandlers.Customer;
public class CustomerCreatedEventHandler : EventHandler<CustomerCreated, ICustomerState>
{
    public CustomerCreatedEventHandler(ICustomerState state, IDp dp) : base(state, dp){}

    public override dynamic Handle(CustomerCreated customerCreated)
    {
        var success = false;
        var customer = customerCreated.Get<Domain.Aggregates.Customer.Customer>();
        
        Dp.State.Customer.Add(customer);

        var destination = Dp.Settings.Default("stream.customerevents");
        var eventName = "CustomerCreated";
        var eventData = new CustomerCreatedEventDTO()
        {
            ID = customer.ID,
            FullName = customer.FullName,
            CustomerTaxID = customer.CustomerTaxID,
            Address = customer.Address,
            Phone = customer.Phone,
            Email = customer.Email,
            CreationDate = customer.CreationDate,
            OrdinalCreationDate = customer.OrdinalCreationDate
        };
        Dp.Stream.Send(destination, eventName, eventData);

        success = true;
        return success;
    }
}

Altere o código do nosso EventHandler conforme modelo abaixo (vide linhas destacadas):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
namespace Application.EventHandlers.Customer;
public class CustomerCreatedEventHandler : EventHandlerWithStateAndExtensions<CustomerCreated, ICustomerState, IExtensions>
{
    public CustomerCreatedEventHandler(ICustomerState state, IExtensions extension, IDp dp) : base(state, extension, dp) { }

    public override dynamic Handle(CustomerCreated customerCreated)
    {
        var success = false;
        var customer = customerCreated.Get<Domain.Aggregates.Customer.Customer>();

        var ordinalCreationDate = Dp.Extensions.HumanService.ToOrdinalWords(customer.CreationDate);
        customer.SetOrdinalDate(ordinalCreationDate);

        Dp.State.Customer.Add(customer);

        var destination = Dp.Settings.Default("stream.customerevents");
        var eventName = "CustomerCreated";
        var eventData = new CustomerCreatedEventDTO()
        {
            ID = customer.ID,
            FullName = customer.FullName,
            CustomerTaxID = customer.CustomerTaxID,
            Address = customer.Address,
            Phone = customer.Phone,
            Email = customer.Email,
            CreationDate = customer.CreationDate,
            OrdinalCreationDate = customer.OrdinalCreationDate
        };

        Dp.Stream.Send(destination, eventName, eventData);

        success = true;
        return success;
    }
}

Testando a aplicação

Agora que temos as implementações necessárias, vamos rodar o microsserviço através do comando abaixo:

Windows

.\run.ps1

Linux / Mac

.\run.sh

No browser, acesse o link: https://localhost:5001/swagger, nós vamos utilizar o método POST para criar um novo customer:

Devprime How To Extensions Key`

Use the Request Body below:

1
2
3
4
5
6
7
{
  "fullName": "Bot One",
  "customerTaxID": "37429734",
  "address": "Av. Paulista, 1811",
  "phone": "11999999999",
  "email": "botone@bot.com"
}

Still in the API use the GET endpoint to get the registered customers (you don’t need to pass any parameters), the result of Response Body should be similar to the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{
  "response": [
    {
      "id": "997809ad-e48f-4eb4-9145-4b569adba492",
      "fullName": "Bot One",
      "customerTaxID": "37429734",
      "address": "Av. Paulista, 1811",
      "phone": "11999999999",
      "email": "botone@bot.com",
      "creationDate": "2022-04-05T03:28:47.16Z",
      "ordinalCreationDate": "April 5th, 2022" //Humanized by our extension
    }
  ],
  "total": 1,
  "pageTotal": 1,
  "hasNext": false,
  "hasPrevious": false,
  "success": true
}

SendGrid

Last modified April 16, 2024 (2b35fcc8)