Application

The Devprime platform provides a complete software architecture strategy, and the “Application” component plays a key role in this approach, as it is responsible for enabling interfaces to the other components in a scenario where the layers are decoupled.

Introduction to Devprime Application

The default structure of the “Application” project starts with a folder called “EventHandlers” and a “EventHandlers.cs” file for registering and mapping the “Domain Event” with the “Event Handler”.

When triggering a “Domain Event” in the business rule, an “Event Handler” will be triggered with the code that interacts with the technology layer.

In addition, there is a folder for the inclusion of the Interfaces, which in this context includes only the “IExtension.cs” file, responsible for enabling customizations of the project through the Adapter Extensions.

Devprime Platform/Architecture/Application

The Application.csproj file contains the default settings for the project and does not need to include any additional dependencies. According to the architectural strategy, this project has a reference to the project Domain to connect to the project’s business rules.

The Version parameter should be updated by the DevOps strategy with the ID of the new version of the microservice application.

The reference related to Devprime Stack should be the same in all projects, and you can update it automatically by running the dp stack, provided by the Devprime CLI and executed in the main project folder.

 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>

Standardized implementations in the Application

One of the important pillars offered by the Devprime platform is the standardization of software development and at this point in the Application project it is already possible to follow some strategies for organizing folders and files that allow you to scale development teams in a standardized way.

The example below depicts an example of including an “Application Services” that receives an event in an API and a MongoDB repository using the State Adapter. This procedure can be done manually by implementing each folder and file or by using the dp init offered by the Devprime CLI that parses the business rules in the Domain and does the implementations.

Important items:

  • In the “Services” folder, the “OrderServices.cs” file which is an “Application Service” has been implemented and in the Model folder in Services/Order/Model the input and output objects “Order.cs and Item.cs” used in the “Application Service”.
  • In the “Interfaces / Services” folder, the “IOrderServices.cs” file that defines the application service interface has been implemented.
  • In the “Interface/Adapters/State” folder, the “IOrderState.cs” interfaces, which act as the HUB for the repositories in the Adapter State, and “IOrderRepository.cs”, which connects to the repository implemented in the State Adapter, have been implemented.
  • In the “Interface / Adapters / Extensions” folder, we find the “IExtensions.cs” interface, which we will not address at this time, as it will be detailed in the Extensions topic.
  • In the “EventHandlers” folder, the “EventHandler.cs” file was implemented, which works as the HUB for mapping Handlers, and the “CreateOrderEventHandler.cs” and “OrderCreatedEventHandler.cs” files that act as Handlers. Also implemented were the models in “EventHandlers / Order / Model”, which are used in conversions and passing parameters through the Handlers.

Devprime Platform / Architecture / Application

Exploring the flow for implementation in Application

The Application project works as a transport layer in this strategy where the software architecture is decoupled and the connection happens through a dependency inversion (DI) strategy.

**#1 - Implementing the “Order.cs / Item.cs” models
In the structure of the Devprime platform, the folder for the implementation of the “Models” for the “Application Services” is located in “Core > Application > Services” and are used for standardization of parameters and mappings.

A common example found in the implementations of these Models is the relationship with business objects present in the Domain project as represented in the code block below. Using this approach, the “Domain” does not know the “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;
    }

For more details explore the two models below:

**#2 - Implementing the “IOrderService.cs” Interface
In the Devprime framework, the folder for implementing the Application Services interfaces is located in the Application project under Core > Application > Interfaces > Services. In the example below, we are demonstrating the “Add/Update/Delete/GetAll” methods.

 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 - Implementing Application Service “OrderService.cs”
In the Devprime platform structure, the folder for the implementation of “Application Services” in the Application project is under “Core > Application > Services”. In the example below, we are reproducing an example of an implementation of the “Add” method where it receives the command, converts it to the domain model. Using the Dp.Attach(order) statement, it adds the object to the “Devprime Pipeline” and then run the Add method.

 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 - Implementation of the “CreateOrderEventHandler.cs” and “OrderCreatedEventHandler.cs” Handlers
In the structure of the Devprime platform, to maintain decoupling, business rules do not communicate directly with technologies. Instead, “Handlers” are implemented in “Core > Application > EventHandlers”, which are used to enable code that reacts to a business fact (“Domain Event”), for example, Dp.ProcessEvent<bool>(new CreateOrder()), emitted within the Domain.

Handler implementation example: CreateOrderEventHandler.

The sample code demonstrates the persistence of an Aggregate in a database by interacting with the Adapter of State. This technological implementation in Application is isolated from 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);
    }
}

Example Handler implementation: OrderCreatedEventHandler.

The sample code enables the emission of an event by allowing the communication of a business fact externally to the microservice, through interaction with the Stream, which integrates seamlessly with RabbitMQ, Kafka, and other platforms. This technological implementation in Application is isolated from 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 - Implementing models used in Event Handlers
In the Devprime framework, the folder for implementing the Models for the EventHandlers is located in the Core > Application > EventHandlers > Order > Model and are used to communicate with the 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 - Registering the Handles for “EventHandler.cs” Domain Events
In the Application layer in “Core / Application / EventHandlers” will be available a HUB for registration of the Handles that will be intercepting the domain events and establishing a relationship between them.

 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 - Implementing Inferface “IOrderState.cs”
The application layer also plays an important role by registering interfaces for interaction with other Adapters, such as the State. The interfaces are organized in the “Core > Application > Interfaces > Adapters” folder structure, and according to this context, we will use a specific folder for the Adapter.

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

Order

Example of Model ‘Order’ deployment for use in App Service.

Item

Example of deploying the Model ‘Item’ for use in App Service.

Last modified November 20, 2024 (61099f59)