In the system I'm currently working on, I'm following SRP (I think!) by separating the validation of domain business rules vs persistence constraints. Let's employ the overused customer example. Say a customer must have a valid zip code, street address and name to satisfy the system's business rules. Let's further say that the customer's selected user name must be unique across all customers, which I define as a persistence constraint. Please consider the following "not ready for production" pseudo code:
public interface IPersistenceValidator<T>
{
bool IsValidForPersistence(T domainObj, IList<ValidationError> validationErrors);
}
public interface IValidatable
{
bool IsValid(IList<ValidationError> validationErrors);
}
public class Customer : IValidatable
{
public bool IsValid(IList<ValidationError> validationErrors)
{
//check for business rule compliance
}
}
public class CustomerDao : IPersistenceValidator<Customer>
{
public bool IsValidForPersistence(Customer domainObj, IList<ValidationError> validationErrors)
{
//check for persistence constraint compliance (user name is unique)
}
public bool SaveCustomer(Customer customer)
{
//save customer
}
}
The classes defined above might get wired up into a service class as follows:
public class SaveCustomerService
{
private CustomerDao _customerDao;
public SaveCustomerService(CustomerDao customerDao)
{
_customerDao = customerDao;
}
public bool SaveCustomer(Customer customer)
{
IList<ValidationError> validationErrors = new List<ValidationError>();
if (customer.IsValid(validationErrors))
{
if (_customerDao.IsValidForPersistence(customer, validationErrors))
{
return _customerDao.SaveCustomer(customer);
}
else
{
return false;
}
}
else
{
return false;
}
}
}
My primary concern with this approach is that future consumers of CustomerDao must know to call IsValidForPersistence() before SaveCustomer(), otherwise invalid data gets persisted. I could create DB constraints to guard against this at the SQL levels, but that feels like a kludge.
It seems like IsValidForPersistence() should be moved into CustomerDao.SaveCustomer() but then I have to refactor the signature of SaveCustomer() to include references to the ValidationErrors class. Before I dive into that big of a refactoring, I wanted to get some feedback from others on common/preffered patterns for dealing with these issues.
Thanks
first check HERE if you want to solve your validation problem like;
public class Address {
#NotNull private String line1;
private String line2;
private String zip;
private String state;
#Length(max = 20)
#NotNull
private String country;
#Range(min = -2, max = 50, message = "Floor out of range")
public int floor;
...
}
anyway you must check username in database. You can customize your validation (like go and check DB for that is unique). Look at another links to detail.
Check hibernate validator
Check Using the Validator framework from jboss
You can read Validation In The Domain Layer partI, partII, this is not java but logic is important.
Related
I want to add 4 eyes principle to ASP.NET Boilerplate framework. That means every change on Role, User,.. need to be approved (by another admin) before applied to the system. I have searched for some time but no answer. So what is the best solution for this flow?
Can I create the same tables with Abp tables (dbo.AbpUser_Temp, etc) and the all the changes will be stored in these tables? Is there any better solution?
Example: In the application, Admin1 has created a user named User1. But this user cannot login to the application until he was approved by Admin2.
Simple Workflows
Example: In the application, Admin1 has created a user named User1. But this user cannot login to the application until he was approved by Admin2.
Simple workflows like these can be appropriately handled by a property and a method:
public class User : AbpUser<User>
{
public bool IsApproved { get; set; }
public void Approve(User approver)
{
if (approver.Id != CreatorUserId)
{
IsApproved = true;
}
}
}
Complex Workflows
Complex workflows like "every change" can do this instead of _Temp tables:
public abstract class ChangeBase : Entity<long>, IExtendableObject
{
public string EntityTypeAssemblyQualifiedName { get; set; }
public string EntityIdJsonString { get; set; }
public long ProposerUserId { get; set; }
public long? ApproverUserId { get; set; }
public string ExtensionData { get; set; }
}
public class Change : ChangeBase
{
[NotMapped]
public Type EntityType => Type.GetType(EntityTypeAssemblyQualifiedName);
[NotMapped]
public object EntityId => JsonConvert.DeserializeObject(EntityIdJsonString, EntityHelper.GetPrimaryKeyType(EntityType));
[NotMapped]
public bool IsApproved => ApproverUserId.HasValue && ApproverUserId != ProposerUserId;
[NotMapped]
public IDictionary<string, string> ChangedPropertyValuePairs => JObject.Parse(ExtensionData).ToObject<Dictionary<string, string>>();
public Change(EntityIdentifier changedEntityIdentifier, long proposerUserId, IDictionary<string, string> changedPropertyValuePairs)
{
EntityTypeAssemblyQualifiedName = changedEntityIdentifier.Type.AssemblyQualifiedName;
EntityIdJsonString = changedEntityIdentifier.Id.ToJsonString();
ProposerUserId = proposerUserId;
ExtensionData = JObject.FromObject(changedPropertyValuePairs).ToString(Formatting.None);
}
public bool Approve(long approverUserId)
{
if (approverUserId != ProposerUserId)
{
ApproverUserId = approverUserId;
return true;
}
return false;
}
}
Usage:
public class UserAppService // ...
{
private readonly IRepository<Change, long> _changeRepository;
public UserAppService(
IRepository<User, long> repository,
IRepository<Change, long> changeRepository) // : base(repository)
{
_changeRepository = changeRepository;
}
public void ChangeUserName(long userId, string newUserName)
{
// Validation, etc.
var changedPropertyValuePairs = new Dictionary<string, string> {
{ nameof(User.UserName), newUserName }
};
var change = new Change(
new EntityIdentifier(typeof(User), userId),
AbpSession.GetUserId(),
changedPropertyValuePairs
);
_changeRepository.Insert(change);
}
public void ApproveChange(long changeId)
{
// Validation, etc.
var change = _changeRepository.Get(changeId);
if (change.EntityType == typeof(User) && change.Approve(AbpSession.GetUserId()))
{
var user = Repository.Get((long)change.EntityId);
var changedPropertyValuePairs = change.ChangedPropertyValuePairs;
foreach (var changedProperty in changedPropertyValuePairs.Keys)
{
switch (changedProperty)
{
case nameof(User.UserName):
user.UserName = changedPropertyValuePairs[changedProperty];
break;
// ...
default:
break;
}
}
}
}
For development
Separate staging and production environments. Develop on one box, test it, get it reviewed and then deploy to a production box. Simple, effective and language agnostic advice.
Since ASP.NET Boilerplate framework included Entity Framework. You could also leverage migrations.
After you do your development work, and requires you to "update-database", then your SOP should be to have the admin review the (relatively simple) migrations that will be committed.
I hope that helps.
For application flow
There are probably quite a few ways to actually implement this so I'll cover a simple one get your idea's flowing, but keep in mind: The way you need to implement two person integrity must fit how your operating procedures should work, and not the other way around. Development doesn't drive business operations, business use-cases drive development.
Extending existing Identity* classes. Example: The ApplicationUser class (it may be named differently, but it derives from IdentityUser
Create 2 flags (boolean fields) that must be, and can only be turned 'on' by an administrator
a single administrator can only turn on 1 flag. (Which means you also have to store which administrator turned on which flag.)
The flags can be stored in the existing Abp* tables, or you can create a new table
Add logic so that the user is not allowed to log in unless those 2 flags are both on.
Example: default IdentityUserRole has identified and registered, but can not log in. Once both admin's switch the flags on, elevate the users IdentityUserRole to a role that is allowed to log in.
consider the following scenario: i have a bean that handles user-searches with a lot of parameters used on many pages with different urls. many users may spent a larger time with custom-searches and currently i am hitting the database to load those static lists everytime.
#ManagedBean
#ViewScoped
public class SearchBean extends DefaultBean {
private String searchPath; //seo: build a url-friendly path depending on search-parameters
private List<Currency>currencies;
private List<Country>countries;
private List<Market>markets;
private List<DrugTypes>drugTypes;
private List<Products>products;
/**
* ...15 other lists
*/
private List<ResultData>results;
#PostConstruct
public void init(){
this.currencies = Currency.getAll(); //jpa-entities
this.countries = Country.getAll();
this.markets = Markets.getAll();
this.drugTypes = DrugTypes.getAll();
this.products = Products.getAll();
}
public String search(){
this.results = ResultData.getByParameters(getSearchParams());
//
//e.g. localhost:8080/myApp/search/markets/germany/class-alpha-products/rhesus?faces-redirect=true
return searchPath;
}
public List<Currency> getCurrencies() { return currencies; }
public void setCurrencies(List<Currency> currencies) { this.currencies = currencies; }
public List<Country> getCountries() { return countries; }
public void setCountries(List<Country> countries) { this.countries = countries; }
public void setMarkets(List<Market> markets) { this.markets = markets; }
public List<Market> getMarkets() { return markets; }
public void setDrugTypes(List<DrugTypes> drugTypes) { this.drugTypes = drugTypes; }
public List<DrugTypes> getDrugTypes() { return drugTypes; }
public List<Products> getProducts() { return products; }
public void setProducts(List<Products> products) { this.products = products; }
}
what is the recommend way regarding to the headline? my small gripe is, that i see 20 jpa-queries on the console although the list-data which is build with <h:selectOneMenu> on client-side does not change on new pages but must be included on every subpage.
leave it as it is
put all those lists as session-attributes and remove them in #predestroy when user leaves.
put the whole bean as sessionbean (i already have 2 session-beans ("user" and "language" and i read that having more is not a good design)
store the list-data as a json-string in a cookie and recreate the list if the cookie exists?
other suggestions?
thanks for watching!
None of all. Caching DB entities isn't the responsibility of a front end (UI) framework. That's the responsibility of the persistence (DB) framework, which is thus JPA in your case.
JPA offers 2nd level caching possibilities. Main advantage as compared to all your proposals is that it knows precisely which entities are more queried and thus need to be cached, and when exactly to invalidate a cached entity because of an entity change. JSF as being a dumb HTML form based MVC framework which only delegates user interface events/data to business services knows nothing of this all.
See also:
How to configure L2 cache in Hibernate/JPA2?
Hibernate 5.1 User Guide - Chapter 12: Caching
EclipseLink Wiki - Examples / JPA / Caching
I am looking for transportation layer for gwt. I would like to create ajax request using generic method, f.e this is my DAO/service:
public class GenericDao<T extends GenericModel<T>> {
private Logger logger = LoggerFactory.getLogger(this.getClass().getCanonicalName());
#Transient protected Class<T> entityClass;
public GenericDao() {
super();
}
public GenericDao(Class<? extends GenericModel<T>> clazz) {
this.entityClass = (Class<T>) clazz;
}
public T getBy(Long id) {
return JPA.em().find(entityClass, id);
}
public List<GenericModel<T>> get() {
logger.error("trying to get data from db");
return getList();
}
public List<GenericModel<T>> getList() {
return JPA.em().createQuery("FROM " + entityClass.getSimpleName()).getResultList();
}
public void save(GenericModel<T> entityClass) {
JPA.em().getTransaction().begin();
JPA.em().persist(entityClass);
JPA.em().getTransaction().commit();
}
public void update(T entityClass) {
JPA.em().getTransaction().begin();
JPA.em().merge(entityClass);
JPA.em().getTransaction().commit();
}
public void delete(T entityClass) {
JPA.em().getTransaction().begin();
JPA.em().remove(entityClass);
JPA.em().getTransaction().commit();
}
}
GenericModel/Entity:
#MappedSuperclass
public class GenericModel<T extends GenericModel<T>> implements Identifiable, Versionable {
#Transient
protected Class<T> entityClass;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Version
private Integer version;
// setter & getter
#Override
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
#Override
public Integer getVersion() {return version;}
public void setVersion(Integer version) {this.version = version;}
// constructor
public GenericModel() {
Class<?> obtainedClass = getClass();
Type genericSuperclass = null;
for (;;) {
genericSuperclass = obtainedClass.getGenericSuperclass();
if (genericSuperclass instanceof ParameterizedType) {
break;
}
obtainedClass = obtainedClass.getSuperclass();
}
ParameterizedType genericSuperclass_ = (ParameterizedType) genericSuperclass;
try {
entityClass = ((Class) ((Class) genericSuperclass_
.getActualTypeArguments()[0]));
} catch (ClassCastException e) {
entityClass = guessEntityClassFromTypeParametersClassTypedArgument();
}
}
public GenericModel(Long id) {
this();
this.id = id;
}
}
I am looking for mechanism that will allow me to use this generic service for all models on client side (each db entity have id- so I would like to downloads using ajax all my Entities this way, so I should have only one generic method for that on client side).
I've already checked:
GWT-RPC
RequestFactory
RestyGWT
But none of them support this feature.
I've found here:
https://www.mail-archive.com/google-web-toolkit#googlegroups.com/msg100095.html
information that: gwt-jackson supports generics and polymorphism. Unfortunately I didn't found any working example that. Can someone help, give an example, approved that information?
All entities have id and version parameter. So I would like to have one metod on client side RF that will allow me to get from server(service/dao/whatever) that entity by id- like this: Request getBy(Long id); But unfortunatelly I can't make it work. I like the RF way, so I've tried it first. Generally I don't wonna repeat code for downloading entity/proxy by id.
For better understanding, please look also on:
RequestFactory client-side inheritance of typed class with generics
I'm confused as to why you think RPC can't handle generics - according to your link, it can, but RestyGWT cannot. Granted, none of your JPA references make any sense in GWT, but those would live in a DAO on the server, not in the entity/model class themselves, or at least not in the client version. If you had a RPC method that returned T where <T extends GenericModel<T>>, then you would have serializers for every possible GenericModel<?> subtype, and any/all that are gwt-compatible could be sent over the wire.
Edit from update to question:
Your GenericModel class uses features of Java that cannot work in GWT, such as reflection. This cannot be compiled to GWT, since the compiler relies on removing reflection information to minimize your compiled size - leaving in general reflection information means leaving in details about all classes and members, even ones that it can't statically prove are in use, since some reflection might make use of them.
If there is a way to phrase your model object in a way that just deals with the data at hand, focus on that. Otherwise consider a DTO which is just the data to send over the wire - I'm not sure how you would plan to use the entityClass field on the client, or why that would be important to read from the superclass's generics instead of just using getClass().
RequestFactory will have a hard time dealing with generics - unlike RPC (and possibly RestyGWT) it cannot handle polymorphism the way you want, but will instead only send the fields for the declared type, not any arbitrary subtype. RPC will actually send the instance if it is something that the client can handle.
I have a repository pattern i created on top of the ado.net entity framework. When i tried to implement StructureMap to decouple my objects, i kept getting StackOverflowException (infinite loop?). Here is what the pattern looks like:
IEntityRepository where TEntity : class
Defines basic CRUD members
MyEntityRepository : IEntityRepository
Implements CRUD members
IEntityService where TEntity : class
Defines CRUD members which return common types for each member.
MyEntityService : IEntityService
Uses the repository to retrieve data and return a common type as a result (IList, bool and etc)
The problem appears to be with my Service layer. More specifically with the constructors.
public PostService(IValidationDictionary validationDictionary)
: this(validationDictionary, new PostRepository())
{ }
public PostService(IValidationDictionary validationDictionary, IEntityRepository<Post> repository)
{
_validationDictionary = validationDictionary;
_repository = repository;
}
From the controller, i pass an object that implements IValidationDictionary. And i am explicitly calling the second constructor to initialize the repository.
This is what the controller constructors look like (the first one creates an instance of the validation object):
public PostController()
{
_service = new PostService(new ModelStateWrapper(this.ModelState));
}
public PostController(IEntityService<Post> service)
{
_service = service;
}
Everything works if i don't pass my IValidationDictionary object reference, in which case the first controller constructor would be removed and the service object would only have one constructor which accepts the repository interface as the parameter.
I appreciate any help with this :) Thanks.
It looks like the circular reference had to do with the fact that the service layer was dependent on the Controller's ModelState and the Controller dependent on the Service layer.
I had to rewrite my validation layer to get this to work. Here is what i did.
Define generic validator interface like below:
public interface IValidator<TEntity>
{
ValidationState Validate(TEntity entity);
}
We want to be able to return an instance of ValidationState which, obviously, defines the state of validation.
public class ValidationState
{
private readonly ValidationErrorCollection _errors;
public ValidationErrorCollection Errors
{
get
{
return _errors;
}
}
public bool IsValid
{
get
{
return Errors.Count == 0;
}
}
public ValidationState()
{
_errors = new ValidationErrorCollection();
}
}
Notice that we have an strongly typed error collection which we need to define as well. The collection is going to consist of ValidationError objects containing the property name of the entity we're validating and the error message associated with it. This just follows the standard ModelState interface.
public class ValidationErrorCollection : Collection<ValidationError>
{
public void Add(string property, string message)
{
Add(new ValidationError(property, message));
}
}
And here is what the ValidationError looks like:
public class ValidationError
{
private string _property;
private string _message;
public string Property
{
get
{
return _property;
}
private set
{
_property = value;
}
}
public string Message
{
get
{
return _message;
}
private set
{
_message = value;
}
}
public ValidationError(string property, string message)
{
Property = property;
Message = message;
}
}
The rest of this is StructureMap magic. We need to create validation service layer which will locate validation objects and validate our entity. I'd like to define an interface for this, since i want anyone using validation service to be completely unaware of the StructureMap presence. Besides, i think sprinkling ObjectFactory.GetInstance() anywhere besides the bootstrapper logic a bad idea. Keeping it centralized is a good way to insure good maintainability. Anyway, i use the decorator pattern here:
public interface IValidationService
{
ValidationState Validate<TEntity>(TEntity entity);
}
And we finally implement it:
public class ValidationService : IValidationService
{
#region IValidationService Members
public IValidator<TEntity> GetValidatorFor<TEntity>(TEntity entity)
{
return ObjectFactory.GetInstance<IValidator<TEntity>>();
}
public ValidationState Validate<TEntity>(TEntity entity)
{
IValidator<TEntity> validator = GetValidatorFor(entity);
if (validator == null)
{
throw new Exception("Cannot locate validator");
}
return validator.Validate(entity);
}
#endregion
}
I'm going to be using validation service in my controller. We could move it to the service layer and have StructureMap use property injection to inject an instance of controller's ModelState to the service layer, but i don't want the service layer to be coupled with ModelState. What if we decide to use another validation technique? This is why i'd rather put it in the controller. Here is what my controller looks like:
public class PostController : Controller
{
private IEntityService<Post> _service = null;
private IValidationService _validationService = null;
public PostController(IEntityService<Post> service, IValidationService validationService)
{
_service = service;
_validationService = validationService;
}
}
Here i am injecting my service layer and validaton service instances using StructureMap. So, we need to register both in StructureMap registry:
ForRequestedType<IValidationService>()
.TheDefaultIsConcreteType<ValidationService>();
ForRequestedType<IValidator<Post>>()
.TheDefaultIsConcreteType<PostValidator>();
That's it. I don't show how i implement my PostValidator, but it's simply implementing IValidator interface and defining validation logic in the Validate() method. All that's left to do is call your validation service instance to retrieve the validator, call the validate method on your entity and write any errors to ModelState.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "PostId")] Post post)
{
ValidationState vst = _validationService.Validate<Post>(post);
if (!vst.IsValid)
{
foreach (ValidationError error in vst.Errors)
{
this.ModelState.AddModelError(error.Property, error.Message);
}
return View(post);
}
...
}
Hope i helped somebody out with this :)
I used a similar solution involving a generic implementor of IValidationDictionary uses a StringDictionary and then copied the errors from this back into the model state in the controller.
Interface for validationdictionary
public interface IValidationDictionary
{
bool IsValid{get;}
void AddError(string Key, string errorMessage);
StringDictionary errors { get; }
}
Implementation of validation dictionary with no reference to model state or anything else so structuremap can create it easily
public class ValidationDictionary : IValidationDictionary
{
private StringDictionary _errors = new StringDictionary();
#region IValidationDictionary Members
public void AddError(string key, string errorMessage)
{
_errors.Add(key, errorMessage);
}
public bool IsValid
{
get { return (_errors.Count == 0); }
}
public StringDictionary errors
{
get { return _errors; }
}
#endregion
}
Code in the controller to copy the errors from the dictionary into the model state. This would probably be best as an extension function of Controller.
protected void copyValidationDictionaryToModelState()
{
// this copies the errors into viewstate
foreach (DictionaryEntry error in _service.validationdictionary.errors)
{
ModelState.AddModelError((string)error.Key, (string)error.Value);
}
}
thus bootstrapping code is like this
public static void BootstrapStructureMap()
{
// Initialize the static ObjectFactory container
ObjectFactory.Initialize(x =>
{
x.For<IContactRepository>().Use<EntityContactManagerRepository>();
x.For<IValidationDictionary>().Use<ValidationDictionary>();
x.For<IContactManagerService>().Use<ContactManagerService>();
});
}
and code to create controllers is like this
public class IocControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return (Controller)ObjectFactory.GetInstance(controllerType);
}
}
Just a quick query on this. It's helped me out quite a lot so thanks for putting the answer up, but I wondered which namespace TEntity exists in? I see Colletion(TEntity) needs System.Collections.ObjectModel. My file compiles without anything further but I see your TEntity reference highlighted in Blue which suggests it has a class type, mine is Black in Visual Studio. Hope you can help. I'm pretty keen to get this working.
Have you found any way to seperate validation into the service layer at all? My gut tells me that validating in the Controller is a bit smelly but I've looked high and low to find a way to pass validation error messages back to the controller without tightly coupling the service layer to the controller and can't find anything. :(
Again, thanks for the great post!
Lloyd
How do you deal with validation on complex aggregates in a domain driven design? Are you consolidating your business rules/validation logic?
I understand argument validation and I understand property validation which can be attached to the models themselves and do things like check that an email address or zipcode is valid or that a first name has a minimum and maximum length.
But what about complex validation that involves multiple models? Where do you typically place these rules & methods within your architecture? And what patterns if any do you use to implement them?
Instead of relying on IsValid(xx) calls all over your application, consider taking some advice from Greg Young:
Don't ever let your entities get into
an invalid state.
What this basically means is that you transition from thinking of entities as pure data containers and more about objects with behaviors.
Consider the example of a person's address:
person.Address = "123 my street";
person.City = "Houston";
person.State = "TX";
person.Zip = 12345;
Between any of those calls your entity is invalid (because you would have properties that don't agree with each other. Now consider this:
person.ChangeAddress(.......);
all of the calls relating to the behavior of changing an address are now an atomic unit. Your entity is never invalid here.
If you take this idea of modeling behaviors rather than state, then you can reach a model that doesn't allow invalid entities.
For a good discussion on this, check out this infoq interview: http://www.infoq.com/interviews/greg-young-ddd
I like Jimmy Bogard's solution to this problem. He has a post on his blog titled "Entity validation with visitors and extension methods" in which he presents a very elegant approach to entity validation that suggest the implementation of a separate class to store validation code.
public interface IValidator<T>
{
bool IsValid(T entity);
IEnumerable<string> BrokenRules(T entity);
}
public class OrderPersistenceValidator : IValidator<Order>
{
public bool IsValid(Order entity)
{
return BrokenRules(entity).Count() == 0;
}
public IEnumerable<string> BrokenRules(Order entity)
{
if (entity.Id < 0)
yield return "Id cannot be less than 0.";
if (string.IsNullOrEmpty(entity.Customer))
yield return "Must include a customer.";
yield break;
}
}
I usualy use a specification class,
it provides a method (this is C# but you can translate it in any language) :
bool IsVerifiedBy(TEntity candidate)
This method performs a complete check of the candidate and its relations.
You can use arguments in the specification class to make it parametrized, like a check level...
You can also add a method to know why the candidate did not verify the specification :
IEnumerable<string> BrokenRules(TEntity canditate)
You can simply decide to implement the first method like this :
bool IsVerifiedBy(TEntity candidate)
{
return BrokenRules(candidate).IsEmpty();
}
For broken rules, I usualy write an iterator :
IEnumerable<string> BrokenRules(TEntity candidate)
{
if (someComplexCondition)
yield return "Message describing cleary what is wrong...";
if (someOtherCondition)
yield return
string.Format("The amount should not be {0} when the state is {1}",
amount, state);
}
For localization, you should use resources, and why not pass a culture to the BrokenRules method.
I place this classes in the model namespace with names that suggest their use.
Multiple model validation should be going through your aggregate root. If you have to validate across aggregate roots, you probably have a design flaw.
The way I do validation for aggregates is to return a response interface that tells me if validation pass/fail and any messages about why it failed.
You can validate all the sub-models on the aggregate root so they remain consistent.
// Command Response class to return from public methods that change your model
public interface ICommandResponse
{
CommandResult Result { get; }
IEnumerable<string> Messages { get; }
}
// The result options
public enum CommandResult
{
Success = 0,
Fail = 1
}
// My default implementation
public class CommandResponse : ICommandResponse
{
public CommandResponse(CommandResult result)
{
Result = result;
}
public CommandResponse(CommandResult result, params string[] messages) : this(result)
{
Messages = messages;
}
public CommandResponse(CommandResult result, IEnumerable<string> messages) : this(result)
{
Messages = messages;
}
public CommandResult Result { get; private set; }
public IEnumerable<string> Messages { get; private set; }
}
// usage
public class SomeAggregateRoot
{
public string SomeProperty { get; private set; }
public ICommandResponse ChangeSomeProperty(string newProperty)
{
if(newProperty == null)
{
return new CommandResponse(CommandResult.Fail, "Some property cannot be changed to null");
}
SomeProperty = newProperty;
return new CommandResponse(CommandResult.Success);
}
}
This questions a bit old now but in case anyone is interested here's how I implement validation in my service classes.
I have a private Validate method in each of my service classes that takes an entity instance and action being performed, if validation fails a custom exception is thrown with the details of the broken rules.
Example DocumentService with built in validation
public class DocumentService : IDocumentService
{
private IRepository<Document> _documentRepository;
public DocumentService(IRepository<Document> documentRepository)
{
_documentRepository = documentRepository;
}
public void Create(Document document)
{
Validate(document, Action.Create);
document.CreatedDate = DateTime.Now;
_documentRepository.Create(document);
}
public void Update(Document document)
{
Validate(document, Action.Update);
_documentRepository.Update(document);
}
public void Delete(int id)
{
Validate(_documentRepository.GetById(id), Action.Delete);
_documentRepository.Delete(id);
}
public IList<Document> GetAll()
{
return _documentRepository
.GetAll()
.OrderByDescending(x => x.PublishDate)
.ToList();
}
public int GetAllCount()
{
return _documentRepository
.GetAll()
.Count();
}
public Document GetById(int id)
{
return _documentRepository.GetById(id);
}
// validation
private void Validate(Document document, Action action)
{
var brokenRules = new List<string>();
if (action == Action.Create || action == Action.Update)
{
if (string.IsNullOrWhiteSpace(document.Title))
brokenRules.Add("Title is required");
if (document.PublishDate == null)
brokenRules.Add("Publish Date is required");
}
if (brokenRules.Any())
throw new EntityException(string.Join("\r\n", brokenRules));
}
private enum Action
{
Create,
Update,
Delete
}
}
I like this approach because it allows me to put all my core validation logic in one place which keeps things simple.