vendredi 16 mars 2018

Domain Design Pattern consideration

I've come up with the following pattern that I would like to use within my domain. My question is more opinion then a single answer.

Use case. Each domain service does one action called submit, a submit takes a Request, it uses the Requirements (validation and getting data) and then gives you a response.

 public interface IDomainService<T, Y, X> 
    where Y : Resources.IBaseRequest
    where T : Resources.IBaseResponse
    where X : Resources.IBaseRequirements
{

    T Submit(Y request);

    X Requirements(Y request);
}

In the code above the definition of IBaseRequest, IBaseResponse, IBaseRequirements is less important, it mainly have common properties but naturally it's important to Identify what the object will be either, a request, response or requirement.

Example of Request, Response and Requirement for adjustment service.

 public interface IAdjustmentRequest : IBaseRequest
{
    AdjustmentType AdjustmentType { get; set; }

    decimal AdjustmentQty { get; set; }

    string TrackingEntityBarcode { get; set; }
}

public interface IAdjustmentRequirements : IBaseRequirements
{
    DomainEntities.TrackingEntity TrackingEntity { get; set; }
}

public interface IAdjustmentResponse : IBaseResponse
{
    DomainEntities.TrackingEntity TrackingEntity { get; set; }
}

At this stage you can see what each object represents and I think this is important for me to give clarity to the domain but not create very specific and defined service interfaces.

The actual implementation will be to take the request, validate the input parameters, fetch the data (entities) and build the requirement object. After validation is done or the Requirements was fulfilled you can Action or persist the requirements and build your response.

At this stage I would like to mention that if I look at the words I choose I cannot help but think maybe I'm missing some old design pattern (I'm little rusty on my domain lingo), but the words I'm referring to is Request, Response and Requirements.

OK so let me continue by showing an Service implemented, note I took out some methods but I want to show the core of it.

  public class AdjustmentService : DomainBaseService<IAdjustmentResponse, IAdjustmentRequest, IAdjustmentRequirements>
{
    protected internal override IAdjustmentRequirements TransactionRequirements(IAdjustmentRequest request)
    {
        Resources.AdjustmentRequirements response = new Resources.AdjustmentRequirements(request);
        try
        {
            //user
            base.RequestUser(request, ref response);

            //inventory
            response.TrackingEntity = this.ValidateTrackingEntity(request.TrackingEntityBarcode);

            //validate qty
            if (request.AdjustmentType == Interface.AdjustmentType.QtyDecrease)
                if (request.AdjustmentQty > response.TrackingEntity.Qty)
                    throw new Exception(ExceptionInsufficientOnhand);

            response.Valid = true;
            return response;
        }
        catch (Exception ex)
        {
            response.ErrorMessages = ex.Message;
            response.Valid = false;
            return response;
        }
    }
    protected internal override IAdjustmentResponse CommitTransaction(IAdjustmentRequirements request)
    {
        try
        {
            Resources.AdjustmentResponse response = new Resources.AdjustmentResponse()
            {
                Transactions = new List<Transaction>(),
                User = request.User
            };

            switch (request.AdjustmentType)
            {
                case Granite.DomainModel.Interface.AdjustmentType.QtyIncrease:
                    request.TrackingEntity.Qty += request.AdjustmentQty;
                    break;
                case Granite.DomainModel.Interface.AdjustmentType.QtyDecrease:
                    request.TrackingEntity.Qty -= request.AdjustmentQty;
                    break;
            }

            Transaction transaction = this.BuildProcessTransaction(request, request.TrackingEntity);

            response.Transactions.Add(transaction);
            response.TrackingEntity = request.TrackingEntity;

            Repository.Session.SaveOrUpdate(transaction);
            Repository.Session.SaveOrUpdate(request.TrackingEntity);

            response.ResponseMessage = string.Format("Adjusted {0} by {1}", request.TrackingEntityBarcode, request.AdjustmentQty);
            return response;
        }
        catch (Exception ex)
        {
            throw new Exception(ex.Message);
        }
    }



}

The two methods are clearly split in responsibility, one does all the validation and if passed the other does the persistence.

I would like to add Adjustment is one of the simple services I deal with. Take note my request and response maps back to models from the UI, the input from the web and the response I like to show.

To conclude I'm asking for advice not a clear answer, opinions and what is the benefits and downfalls of taking a approach like this.

Aucun commentaire:

Enregistrer un commentaire