I am coding a web based solution which consists of N number of tiers, these are:
- UI
- Web API
- Business
The question I have is related to validation, repository and CRUD operations. I am adopting the command / query pattern whereby I have my repositories for data retrieval only (see below) and its query dependencies injected in. I have seen people use repositories for CRUD too, but then I get the impression it is doing too much thus warrants further abstraction.
public interface IAddressRepository
{
Address GetById(int id);
IEnumerable<Address> GetByPersonId(int personId);
IEnumerable<Address> GetByPostalCode(string postalCode);
}
public AddressRepository(IGetAddressByIdQuery getAddressByIdQuery
, IGetAddressesByPersonIdQuery getAddressesByPersonIdQuery
, IGetAddressesByPostalCodeQuery getAddressesByPostalCodeQuery)
{
this.getAddressByIdQuery = getAddressByIdQuery;
this.getAddressesByPersonIdQuery = getAddressesByPersonIdQuery;
this.getAddressesByPostalCodeQuery = getAddressesByPostalCodeQuery;
}
Should my repository return validation errors? For example, the GetById(int id)
method will be checking if the id > 0
then make a DB call or simply return a new instance, likewise with a postal code lookup. However, I also think going by the SOLID design principle everything should be autonomous thus this approach of returning no validation errors is the correct choice. I choose to NOT return null on all my methods simply because checking for null is considered as an anti-pattern.
The encompassing of the CRUD logic. I have seen uses of a Manager
and/or Service
classes, for example:
public interface IAddressManager
{
IEnumerable<Error> Delete(Address address);
IEnumerable<Error> Save(Address address);
IEnumerable<Error> IsValid(Address address);
}
public AddressManager(IAddressValidator addressValidator, IAddressCommand addressCommand)
{
this.addressValidator = addressValidator;
this.addressCommand = addressCommand;
}
Is the general consensus to have a manager class which encompasses these operations but have the dependencies (the commands injected in)? This way, before I call save()
, I validate the object before persisting it. I can also control this behavior via TDD.
Query code example:
public interface IGetAddressByIdQuery
{
Address Execute(int id);
}
public class GetAddressByIdQuery : IGetAddressByIdQuery
{
private readonly string connectionString;
public GetAddressByIdQuery(string connectionString)
{
this.connectionString = connectionString;
}
public Address Execute(int id)
{
using (var conn = new SqlConnection(this.connectionString))
{
return conn.Get<Address>(new { Id = id });
}
}
}
The data access tier is not apparent as at the moment as the business library is fairly small. I am a firm believer in not creating several libraries for the sake of it. Only create them when it is warranted to do so, i.e. a library is getting too big.
What are your thoughts as to whether or not a better approach / pattern exists?
IGetAddressByIdQuery
and likewise are in your constructor? \$\endgroup\$