3
\$\begingroup\$

I've come up with the following pattern that I would like to use within my domain. My question is more opinion than an 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 and IBaseRequirements are less important as they mainly have common properties but generally, it's important to identify whether the object will be either a request, response or requirement.

An example of a 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 take the request, validate the input parameters, fetch the data (entities) and build the requirement object. After validation is done or if the requirements were 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 chose, 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 are Request, Response and Requirements.

OK so let me continue by showing a 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 terms of responsibility; one does all the validation and if passed the other does the persistence.

I would like to mention that Adjustment is one of the simple services I deal with and it's worthy to note here, that my request and response maps back to models from the UI, the input from the web and the response I like to show.

In conclusion, I'm asking for advice, not a clear answer or opinions and also what are the benefits and downfalls of taking an approach like this.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I think that your approach is fine. However, I would mention two points that come to mind from the code below:

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;
}
  1. On checking if stock is available, an exception is thrown (pretty severe) if not enough, whereas when an exception is caught, a response is sent back without raising the exception.

    In this case I would send a response back for both, or at the very least, swap them around (throw the caught exception, send response with message that there is not enough stock).

  2. It might be nice to use Dependency Injection to enable swapping out the hard coded implementations of AdjustmentResponse and AdjustmentRequirements etc.

    This would allow for two things; Alternate implementations, and Unit Testing from a small implementation of the interface.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.