Application

A plataforma Devprime disponibiliza uma estratégia completa de arquitetura de software, e o componente “Application” desempenha um papel fundamental nessa abordagem, pois é responsável por habilitar interfaces para os outros componentes em um cenário em que as camadas são desacopladas.

Introdução ao Devprime Application

A estrutura padrão do projeto “Application” inicia com uma pasta chamada “EventHandlers” e um arquivo “EventHandlers.cs” para registro e mapeamento do “Domain Event” com o “Event Handler”.

Ao acionar um “Domain Event” na regra de negócio um “Event Handler” será acionado com o código que interage com a camada de tecnologia.

Além disso, há uma pasta para inclusão das Interfaces, que neste contexto inclui apenas o arquivo “IExtension.cs”, responsável por habilitar customizações do projeto por meio do Adapter Extensions.

Devprime Plataform / Architecture / Application

O arquivo Application.csproj traz as configurações padrões do projeto e não é necessário incluir nenhuma dependência adicional. De acordo com a estratégia de arquitetura, esse projeto possui uma referência ao projeto Domain para conexão com as regras de negócio do projeto.

O parâmetro Version deve ser atualizado pela estratégia de DevOps com o ID da nova versão da aplicação do microsserviço.

A referência relacionada a Devprime Stack deve ser a mesma em todos os projetos, e você pode atualizá-la automaticamente executando o comando dp stack, disponibilizado pelo Devprime CLI e executado na pasta principal do projeto.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// Application.csproj
<Project Sdk="Microsoft.NET.Sdk">
	<PropertyGroup>
		<TargetFramework>net7.0</TargetFramework>
		<Version>1.0.0.0</Version>
	</PropertyGroup>
	<PropertyGroup>
      <ProjectGuid>{1ebcdb06-5294-4b06-9c45-1a8d299278d1}</ProjectGuid>
    </PropertyGroup>		
	<ItemGroup>
		<PackageReference Include="devprime.stack.foundation" Version="7.0.59" />
  	</ItemGroup>
	<ItemGroup>
		<ProjectReference Include="..\Domain\Domain.csproj" />
	</ItemGroup>
</Project>

Implementações padronizadas no Application

Um dos pilares importantes oferecidos pela plataforma Devprime é a padronização do desenvolvimento do software e nesse momento no projeto Application já é possivel acompanhar algumas estratégias de organização de pastas e arquivos que permitem escalar as equipes de desenvolvimento de forma padronizada.

O exemplo abaixo retrata um exemplo de inclusão de um “Application Services” que recebe um evento em uma API e um repositório do MongoDB utilizando o Adapter de State. Esse procedimento pode ser manual implementando cada pasta e arqivo ou utilizando o comando dp init oferecido pelo Devprime CLI que analisa as regras de negócio no Domain e faz as implementações.

Itens importantes:

  • Na pasta “Services”, foi implementado o arquivo “OrderServices.cs” que é um “Application Service” e na pasta Model em Services / Order / Model os objetos de entrada e saída “Order.cs e Item.cs” utilizados no “Application Service”.
  • Na pasta “Interfaces / Services”, foi implementado o arquivo “IOrderServices.cs” que define a interface do serviço de aplicação.
  • Na pasta “Interface / Adapters / State”, foram implementadas as interfaces “IOrderState.cs”, que atuam como o HUB para os repositórios no Adapter State, e “IOrderRepository.cs”, que se conecta com o repositório implementado no Adapter de State.
  • Na pasta “Interface / Adapters / Extensions”, encontramos a interface “IExtensions.cs”, a qual não abordaremos neste momento, pois será detalhada no tópico de Extensions.
  • Na pasta “EventHandlers”, foi implementado o arquivo “EventHandler.cs”, que funciona como o HUB para mapeamento de Handlers, e os arquivos “CreateOrderEventHandler.cs” e “OrderCreatedEventHandler.cs” que atuam como Handlers. Também foram implementados os modelos em “EventHandlers / Order / Model”, que são usados nas conversões e passagem de parâmetros pelos Handlers.

Devprime Plataform / Architecture / Application

Explorando o fluxo para implementação no Application

O projeto de Application funciona como uma camada de transporte nessa estratégia onde a arquitetura de software é desacoplada e a conexão acontece por meio de uma estratégia de inversão de dependência (DI).

#1 - Implementando os models Order.cs / Item.cs
Na estrutura da plataforma Devprime, a pasta para a implementação dos “Models” para os “Application Services” está localizada em “Core > Application > Services” e são utilizados para padronização dos parâmetros e mapeamentos.

Um exemplo comum encontrado nas implementações dessas Models é o relacionamento com objetos de negócio presentes no projeto Domain conforme representado no bloco de código abaixo. Utilizando essa abordagem o “Domain” não conhece o “Application”.

1
2
3
4
5
6
    public virtual Domain.Aggregates.Order.Order ToDomain(Guid id)
    {
        var _order = new Domain.Aggregates.Order.Order();
        _order.ID = id;
        return _order;
    }

Para mais detalhes explore os dois modelos abaixo:

#2 - Implementando a Interface IOrderService.cs
Na estrutura da plataforma Devprime, a pasta para a implementação das interfaces dos “Application Services” está localizada no projeto “Application” em “Core > Application > Interfaces > Services”. No exemplo abaixo, estamos demonstrando os métodos “Add / Update / Delete / Get / GetAll”.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Interface: IOrderService.cs
namespace Application.Interfaces.Services;
public interface IOrderService
{
    void Add(Application.Services.Order.Model.Order command);
    void Update(Application.Services.Order.Model.Order command);
    void Delete(Application.Services.Order.Model.Order command);
    Application.Services.Order.Model.Order 
    Get(Application.Services.Order.Model.Order query);
    PagingResult<IList<Application.Services.Order.Model.Order>>
    GetAll(Application.Services.Order.Model.Order query);
}

#3 - Implementando o Application Service OrderService.cs
Na estrutura da plataforma Devprime, a pasta para a implementação dos “Application Services” no projeto Application está em “Core > Application > Services”. No exemplo abaixo, estamos reproduzindo um exemplo de implementação do método “Add” onde ele recebe o command, converte no modelo de dominio. Ao utilizar a instrução Dp.Attach(order) ela adiciona o objeto ao “Devprime Pipeline” e depois executada o metodo Add.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Application Services: OrderService.cs
namespace Application.Services.Order;
public class OrderService : ApplicationService<IOrderState>, IOrderService
{
    public OrderService(IOrderState state, IDp dp) : base(state, dp)
    {
    }
    public void Add(Model.Order command)
    {
        Dp.Pipeline(Execute: () =>
        {
            var order = command.ToDomain();
            Dp.Attach(order);
            order.Add();
        });
    }
    
}

#4 - Implementação dos Handlers CreateOrderEventHandler.cs e OrderCreatedEventHandler.cs
Na estrutura da plataforma Devprime, para manter o desacoplamento, as regras de negócio não se comunicam diretamente com as tecnologias. Em vez disso, são implementados os “Handlers” em “Core > Application > EventHandlers”, que são usados para habilitar o código que reage a um fato de negócio (“Domain Event”), por exemplo, Dp.ProcessEvent<bool>(new CreateOrder()), emitido dentro do Domain.

Exemplo de implementação do Handler: CreateOrderEventHandler.

O código de exemplo demonstra a persistência de um Aggregate em um banco de dados por meio da interação com o Adapter de State. Essa implementação tecnológica no Application está isolada do Domain.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Application Handler: CreateOrderEventHandler
namespace Application.EventHandlers.Order;
public class CreateOrderEventHandler : EventHandler<CreateOrder, IOrderState>
{
    public CreateOrderEventHandler(IOrderState state, IDp dp) : base(state, dp)
    {
    }
    public override dynamic Handle(CreateOrder createOrder)
    {
        var order = createOrder.Get<Domain.Aggregates.Order.Order>();
        return Dp.State.Order.Add(order);
    }
}

Exemplo de implementação do Handler: OrderCreatedEventHandler.

O código de exemplo habilita a emissão de um evento permitindo a comunicação de um fato de negócio externamente ao microsserviço, por meio da interação com o Adapter de Stream, que se integra de forma transparente com o RabbitMQ, Kafka e outras plataformas. Essa implementação tecnológica no Application está isolada do Domain.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Application Handler: OrderCreatedEventHandler
namespace Application.EventHandlers.Order;
public class OrderCreatedEventHandler : EventHandler<OrderCreated,
 IOrderState>
{
    public OrderCreatedEventHandler(IOrderState state, IDp dp)
     : base(state, dp)
    {
    }
    public override dynamic Handle(OrderCreated orderCreated)
    {
        var success = false;
        var order = orderCreated.Get<Domain.Aggregates.Order.Order>();
        var destination = Dp.Settings.Default("stream.orderevents");
        var eventName = "OrderCreated";
        var eventData = new OrderCreatedEventDTO()
        {ID = order.ID, CustomerName = order.CustomerName,
        CustomerTaxID = order.CustomerTaxID, Total = order.Total};
        Dp.Stream.Send(destination, eventName, eventData);
        success = true;
        return success;
    }
}

#5 - Implementando models utilizados no Event Handlers
Na estrutura da plataforma Devprime, a pasta para a implementação dos “Models” para os “EventHandlers” está localizada no em “Core > Application > EventHandlers > Order > Model” e são utilizadas na comunicação com os Adapters.

1
2
3
4
5
6
7
8
9
// Model: CreateOrderEventModel
namespace Application.Services.Order.Model;
public class CreateOrderEventDTO
{
    public Guid ID { get; set; }
    public string CustomerName { get; set; }
    public string CustomerTaxID { get; set; }
    public double Total { get; set; }
}
1
2
3
4
5
6
7
8
9
//Model: OrderCreatedEventDTO
namespace Application.Services.Order.Model;
public class OrderCreatedEventDTO
{
    public Guid ID { get; set; }
    public string CustomerName { get; set; }
    public string CustomerTaxID { get; set; }
    public double Total { get; set; }
}

#6 - Registrando os Handles para Domain Events EventHandler.cs
Na camada de Application em “Core / Application / EventHandlers” estará disponivel um HUB para registro dos Handles que estarão interceptando os eventos de dominio e estabelecendo um relacionamento entre ambos.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//HUB: EventHandler
Core > Application > EventHandlers
namespace Application.EventHandlers;
public class EventHandler : IEventHandler
{
    public EventHandler(IHandler handler)
    {
        handler.Add<CreateOrder, CreateOrderEventHandler>();
        handler.Add<OrderCreated, OrderCreatedEventHandler>();
        
    }
}

#7 - Implementando a Inferface IOrderState.cs
A camada de aplicação também desempenha um papel importante registrando interfaces para interação com outros Adapters, como o State. As interfaces estão organizadas na estrutura de pastas “Core > Application > Interfaces > Adapters”, e, de acordo com esse contexto, utilizaremos uma pasta específica para o Adapter.

1
2
3
4
5
6
//Inferface: IOrderState
namespace Application.Interfaces.Adapters.State;
public interface IOrderState
{
    IOrderRepository Order { get; set; }
}

Order

Exemplo de implementação da Model Order para utilização no Serviço de Aplicação.

Item

Exemplo de implementação da Model Item para utilização no Serviço de Aplicação.

Última modificação October 26, 2023 (795e8af6)