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 logging 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 code that interacts with the technology layer.

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

Devprime Platform/Architecture/Application

The Application.csproj file contains the default project settings and you don’t need to include any additional dependencies. According to the architecture strategy, this project has a reference to the project Domain to connect to the project’s business rules.

The parameter Version 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 command dp stack, provided by the Devprime CLI and run in the project’s main 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 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 the development teams in a standardized way.

The example below depicts an example of including an “Application Services” that receives an event in a MongoDB API and 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 business rules in Domain and makes 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 under 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 cover at this time, as it will be detailed in the Extensions topic.
  • In the “EventHandlers” folder, the “EventHandler.cs” file has been implemented, which works as the HUB for Handler mapping, 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 by Handlers.

Devprime Platform/Architecture/Application

Exploring the Flow for Application Deployment

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 Interface IOrderService.cs
In the Devprime platform structure, 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/Get/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’re reproducing an example implementation of the “Add” method where it takes the command, converts it to the domain model. When 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 - Implement Handlers CreateOrderEventHandler.cs and OrderCreatedEventHandler.cs
In the structure of the Devprime platform, to maintain decoupling, the business rules do not communicate directly with the technologies. Instead, “Handlers” are implemented in “Core > Application > EventHandlers”, which are used to enable code that reacts to a business fact (“Domain Event”), e.g. Dp.ProcessEvent<bool>(new CreateOrder()), emitted within the Domain.

Example Handler implementation: CreateOrderEventHandler.

The sample code demonstrates the persistence of an Aggregate in a database by interacting with the Adapter of State. This technological implementation in the Application is isolated from the 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 emitting of an event by allowing the communication of a business fact externally to the microservice, through interaction with the Stream, which seamlessly integrates with RabbitMQ, Kafka, and other platforms. This technological implementation in the Application is isolated from the 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 platform structure, the folder for the implementation of the “Models” for the “EventHandlers” is located in the “Core > Application > EventHandlers > Order > Model” and are used in communication 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 Handles for Domain Events EventHandler.cs
In the Application layer in “Core / Application / EventHandlers” a HUB will be available to register 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 in registering interfaces for interaction with other Adapters, such as 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 implementation of Model ‘Order’ for use in App Service.

Item

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

Last modified April 16, 2024 (2b35fcc8)