1

I am creating a library to interact with third party api.This library will be wrapper around that third party library and i want to expose my wrapper methods to client(webapi,winform,console,mvc etc..).

Below are the methods that i want to expose to my clients to perform operations using third party apis but i dont want to give them direct access.They will always use my wrapper api to perform operations.

public interface IMyLibraryWrapperApi
    {
             //methods expost to clients
         int AddRegion(RegionRequest request);
    }

    public class MyLibraryWrapperApi : IMyLibraryWrapperApi
    {   
        private readonly IThirdPartyUnitOfWork _thirdPartyUnitOfWork;
        private readonly IRegionServices _regionService;
        public MyLibraryWrapperApi(string domain, string username,string password)
        {
            this._thirdPartyUnitOfWork = new ThirdPartyUnitOfWork(domain, username,password);
            this._regionService = new RegionServices(_thirdPartyUnitOfWork);
        }
        public int AddRegion(RegionRequest request)
        {
           return _regionService.CreateRegion(RegionRequest request);
        }
    }

Service/Business Layer :

public class RegionService : IRegionService
    {
        private readonly IThirdPartyUnitOfWork _thirdPartyUnitOfWork;
        public RegionService(IThirdPartyUnitOfWork thirdPartyUnitOfWork)
        {
            this._thirdPartyUnitOfWork = thirdPartyUnitOfWork;
        }
        public void CreateRegion(RegionRequest request)
        {
            _thirdPartyUnitOfWork.Insert(request.Name,request.Direction);
        }
    }

DataAccess Layer :

public interface IThirdPartyUnitOfWork 
    {
        int Insert(string name,string direction);
        T GetById(int id);
    }

    public class ThirdPartyUnitOfWork : IThirdPartyUnitOfWork, IDisposable
    {
        private readonly ServiceContext _serviceContext;
        public ThirdPartyUnitOfWork(string domain, string username, string password)
        {
            _serviceContext = new ServiceContext(domain);
            _serviceContext.Credentials = new ThirdPartyCredentials(username, password);
        }
        //Insert method implementation
        //GetById method implementation
    }

Now I want that IMyLibraryWrapperApi should always interact with Service Layer and Service layer will interact with data access layer but here as you can see that IThirdPartyUnitOfWork is being exposed in IMyLibraryWrapperApi and even any client can call IThirdPartyUnitOfWork which i dont want.

But with current design I am not getting how to design this layer properly so that they do not leak in to other layers.

Can anybody please help me with some suggestions to improve this design

  • 1
    "as you can see that IThirdPartyUnitOfWork is being exposed in IMyLibraryWrapperApi". How exaxctly? Your IMyLibraryWrapperApi inteface has just the method exposed. The user doesn't have any information about the implementation – Shai Aharoni Dec 28 '18 at 8:59
1

First of all, I am also in the process of overcoming overthinking and confusion :)

Now, onto the real deal, you are over complicating your design. Let's take it from scratch and see where you want to go and what you have at your disposal to get there.

1. What you want
You want that, given a RegionRequest, you want something that will take that request and somehow persist it. At this moment, you don't care how it will do it, what sort of technology it will use, you just want it done. The interface (and part of the contract) is

public interface IRegionService
{
    int AddRegion(RegionRequest regionRequest);
}

This is all the client classes that want to send a region request need to know about a region service. Anything more than this is a leaky abstraction!

2. What you have
You know for sure that you will be using a certain framework for storing that information - but you don't want any project or class needlessly knowing about it.

Some general theory
It is vital that you get assembly dependencies correctly!
I won't rant too much about this, but instead redirect you to a very concise and well-written explanation of architectural dependency management.

The idea is that you want to keep your Domain/Business project totally in the dark regarding what technologies (be them data or presentation) you will be using. The only projects that should have any knowledge about third party software or more low-level implementations should be the ones that adapt these tools to your domain's needs and the project where your composition root resides (so it can actually specify an implementation for an interface inside your domain).

Going back to your application's domain
Any client class that wishes to add a region (be it webapi,winform,console,mvc etc), should only depend on the interface above!
(As an aside, once you have decided this you can and should go ahead and create unit tests for those clients and check whether they will call this interface, that you will have mocked in unit tests).
For example, your WebApi controller might look like this:

public sealed class RegionController : ApiController
{
    private readonly IRegionService regionService;

    public RegionController(IRegionService regionService)
    {
        this.regionService = regionService;
    }

    public IHttpActionResult Post(int someId, string someName)
    {
        var request = new RegionRequest(someId, someName);
        var result = this.regionService.AddRegion(request);

        if (result == 0)
            return this.Ok();
        else if (result == -1)
            return this.Conflict();
        else
            return this.BadRequest();
    }

}

With the above in mind let's take a look at how your project dependencies

Project dependencies

Note that all dependencies point toward the business project. They all must conform to the interfaces it is dictating. It is the one telling the client (WebApi, WCF, etc.) what it can do and telling the data libraries (EF, WCF, etc.) what the data they should be supplying must look like.

Putting the third party data access to work
Now that we have defined the client and the business/service/domain layer, all that is left is to implement something that will actually persist that RegionRequest. In order to do that, we must adapt that third party library to our needs - more precisely, to the contract specified by your IRegionService, RegionRequest and the accompanying unit tests.

public class SomeThirdPartyRegionService : IRegionService
{
    private SomeUnitOfWork someUnitOfWork;

    public SomeThirdPartyRegionService(SomeUnitOfWork someUnitOfWork)
    {
        this.someUnitOfWork = someUnitOfWork;
    }

    public int AddRegion(RegionRequest regionRequest)
    {
        try
        {
            this.someUnitOfWork.Insert(regionRequest);
            return 0;
        }
        catch (SomeException e)
        {
            return -1;
        }
    }
}


Based on the simple example you have given, this should be it!
Inside your business layer you'll have the RegionRequest and IRegionService and inside the ThirdPartyAdapter project you'll have that adapter class. Nothing more, since this service of yours seems to be doing simple CRUD operations.

But what if I need to add some more business logic, like validation? Where do I put that?
Suppose you have to do some validation on that request - ensure that the parameters inside at least adhere to a given format (for example an ISO code).
The place where this kind of logic, namely domain logic, resides is of course... the domain layer. Now, you have 2 choices: add this logic to the RegionRequest object (in the constructor or otherwise) or inside a new composite implementation of the IRegionService:

public class RegionService : IRegionService
{
    private readonly IRegionService regionServiceImplementation;

    public RegionService(IRegionService regionServiceImplementation)
    {
        this.regionServiceImplementation = regionServiceImplementation;
    }

    /// <inheritdoc />
    public int AddRegion(RegionRequest regionRequest)
    {
        if (!Regex.IsMatch(regionRequest.SomeCode, "SomePattern"))
            return -2;

        return this.regionServiceImplementation.AddRegion(regionRequest);
    }
}

I won't go into the validation example inside the RegionRequest, since it is fairly trivial.

For the grand finale, this is what your high-level view of your solution should look like: Final state of the solution
And the composition root (of a console app), the place where all the interfaces get to meet their implementations

public class Program
{
    public static void Main()
    {
        var someUnitOfWork = new SomeUnitOfWork();
        var someThirdPartyRegionService = new SomeThirdPartyRegionService(someUnitOfWork);
        var service = new RegionService(someThirdPartyRegionService);

        var controller = new RegionController(service);
    }
}
  • Thank you so much for putting up a lot of efforts and work to share this design.Really really appreciate it and sorry for the late reply. – Learning-Overthinker-Confused Jan 11 at 15:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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