Currently I'm facing a trouble implementing the business logic using clean architecture and cqrs pattern. I'm using Mediatr and AutoMapper to do that. I have the following structure for the cqrs pattern
- ApplicationCore
- DTOs
- SmartphoneDto.cs
- Features
- Smartphone
- Handlers
- Commands
- AddSmartphoneCommandHandler.cs
- Queries
- Commands
- Requests
- Commands
- AddSmartphoneCommand.cs
- Queries
- Commands
- Handlers
- Smartphone
- DTOs
- DomainCore
- Smartphone.cs
And the following classes:
SmartphoneDto.cs
public class SmartphoneDto
{
public int Id { get; set; }
public string serialnumber { get; set; }
}
Domain Entity Smartphone.cs
public class Smartphone
{
public int Id { get; set; }
public string imei { get; set; }
public string serialnumber { get; set; }
public string calculatedValue { get; set; }
}
AddSmartphoneCommand.cs
public class AddSmartphoneCommand : IRequest<bool>
{
public SmartphoneDto SmartphoneDto { get; set; }
}
AddSmartphoneCommandHandler.cs
public class AddSmartphoneCommandHandler : IRequestHandler<AddSmartphoneCommand, bool>
{
private readonly ISmartphoneRepository _smartphoneRepository;
private readonly IMapper _mapper;
public AddSmartphoneCommandHandler(ISmartphoneRepository smartphoneRepository, IMapper mapper)
{
_smartphoneRepository = smartphoneRepository;
_mapper = mapper;
}
public async Task<bool> Handle(AddSmartphoneCommand request, CancellationToken cancellationToken)
{
var smartphone = _mapper.Map<Smartphone>(request.SmartphoneDto);
//Business Logic???
await _smartphoneRepository.AddAsync(smartphone);
return true;
}
}
And I´m not sure where the business logic goes, I've read different post with suggestions like these but with no possible solutions or a clear example:
Post 1 As I understood we can put business logic in a external class that works as a service called from the handle method of the command handler
Post 2 But in this post shows that the command handler doesn't have to contain any logic.
Due to my confusion, I've thinked put the business logic as follow:
{
private readonly ISmartphoneRepository _smartphoneRepository;
private readonly IMapper _mapper;
public AddSmartphoneCommandHandler(ISmartphoneRepository smartphoneRepository, IMapper mapper)
{
_smartphoneRepository = smartphoneRepository;
_mapper = mapper;
}
public async Task<bool> Handle(AddSmartphoneCommand request, CancellationToken cancellationToken)
{
var smartphone = _mapper.Map<Smartphone>(request.SmartphoneDto);
#region businesslogic
//All the business logic in this place
smartphone.imei = someApi.GetAsync("someurl", smartphone.serialnumber);
smartphone.calculatedValue = (smartphone.serialnumber - smartphone.imei) * 0.05;
//end business logic
#endregion
await _smartphoneRepository.AddAsync(smartphone);
return true;
}
}
What would be the correct place in the structure to put that kind of business logic?
Commands and Queries should be under the Application Layer.
DEPENDENCIES BETWEEN LAYERS:
Doesn't depend on any layer.
Contains: Entities, Aggregates, Value Objects, Repositories contracts.
Depends on the Domain and Infrastructure layers.
Contains: CQRS, Use Cases, API contracts & implementations.
Depends on the Domain layer.
Contains: Repositories implementation, ORMs (e.g., Entity Framework), Cryptography, Logging, etc.
Depends on the Application layer.