Reputation: 449
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
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?
Upvotes: 1
Views: 520
Reputation: 6976
The best place for business logic is in the domain layer itself.
Your domain objects are currently what we call "anemic" models, meaning they have no encapsulation whatsoever (just a bag of properties with getters and setters that don't enforce any business rules).
It sounds like setting the imei
and perhaps the serialnumber
should update the calculatedValue
. The code can be moved to the domain by encapsulating the logic in a computed property, like so:
public class Smartphone
{
public int Id { get; set; }
public string serialnumber { get; set; }
public string imei { get; set; }
public string calculatedValue => (this.serialnumber - this.imei) * 0.05;
}
This is one way to do it. There is a lot more you could encapsulate into the Smartphone
class but that's a start.
Upvotes: 0
Reputation: 11
Commands and Queries should be under the Application Layer.
Upvotes: 1