I am currently working on a ASP.NET 8 MVC project where we decided to use the Controller-Service-Repository pattern.
At the moment there is not a lot of business logic yet in the MVC Service layer, so mostly it calls the repository and nothing else.
The repository calls an API. This is a SOAP API, so I cannot rely on HTTP errors there. I only get a “Code” where 0 is OK.
A lot of API methods have rather complex business logic, so it is a lot of work to implement the same logic in the MVC Service layer, if not impossible because we simple cannot get all the data from the API. So we rely on the API mostly for business logic errors.
When I believe there is an error in the API I throw a RepositoryException.
Then however I am uncertain what would be the best approach to send this to the client.
Should I handle repository exceptions in the Service layer with a try catch?
Should I then do the same thing to the Controller with the ServiceException (which does not exist yet) and send this to the View?
I could use some advice regarding this.
This is an example of how I am handling it at the moment.
The repository:
public class ContactPersonRepository : SoapRepository, IContactPersonRepository
{
public ContactPersonRepository(ISoapService client, ISoapAuthentication authentication) : base(client, authentication)
{
}
public async Task<ContactPerson> UpdateAsync(ContactPerson contactPerson)
{
var response = await client.UpdateContactPersonAsync(DeviceId, AuthToken, contactPerson);
if (response.HasErrors())
{
throw new SoapRepositoryException(response);
}
return response.ContactPerson;
}
}
The HasErrors() class is an extension method which simply filters out errors by Code. For example, Code 2 means that there are no results in the list. I don’t see this as a real error and ignore it.
The Extension method HasErrors():
public static class ServiceResponseExtensions
{
public static bool HasErrors(this ServiceResponse response)
{
int[] errorCodeWhitelist = [0, 1, 2, 10];
return !errorCodeWhitelist.Contains(response.Error);
}
}
The Service:
public class ContactPersonService : IContactPersonService
{
private IContactPersonRepository contactPersonRepository;
public ContactPersonService(IContactPersonRepository contactPersonRepository)
{
this.contactPersonRepository = contactPersonRepository;
}
public async Task<ContactPerson> UpdateAsync(ContactPerson contactPerson)
{
return await contactPersonRepository.UpdateAsync(contactPerson);
}
}
The Controller:
public class ContactPersonController : BaseController
{
private IContactPersonService contactPersonService;
public ContactPersonController(IContactPersonService contactPersonService)
{
this.contactPersonService = contactPersonService;
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(ContactPersonEditModel model)
{
if (ModelState.IsValid)
{
ContactPerson toUpdate = model.MapTo<ContactPerson>();
await contactPersonService.UpdateAsync(toUpdate);
return RedirectToAction("Index");
}
return View(model);
}
}
Valition is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.