Question

I'm working on decoupling a Xamarin (.NetStandard) solution. It was using DryIOC container but I didn't see the IOC structure from the dependency, as the projects are still tightly coupled.

Three main projects go as below:
MyApp -> MyCommon -> MyAPI

namespace MyAPI
{
    public interface IApiHelper //Q1
    {
        Task<string> CheckSessionAsync();
    }

    public class ApiHelper : IApiHelper, IDisposable
    {
        public async Task<string> CheckSessionAsync()
        {
            //do something
        }
    }
}
namespace MyCommon
{
    public static class AssemblyInfo
    {
        public static void IocRegister()
        {
            MyIoc.Current.Register<IApiHelper, ApiHelper>();
            MyIoc.Current.Register<IMyService, MyService>(); //Q2
        }
    }
}
namespace MyCommon
{
    public class MyService : IMyService
    {
        public MyService(IApiHelper helper)
        {
            Helper = helper;
        }
    }

    public interface IMyService
    {
    }
}
namespace MyApp
{
    public class AppPage
    {
        public AppPage()
        {
            MyService = MyIoc.Current.Resolve<IMyService>();
        }

        public void OnAppearing()
        {
            var checkResult = MyService.Helper.CheckSessionAsync(); //Q3
        }
    }
}

My questions are:

  1. Is it ok to move IApiHelper into project MyCommon, so the dependency will be an inversion. Do I need an individual project to contain all the interfaces for better structure if there are heaps?
  2. Is it ok to separate the registration into different classes? If I register MyService in assembly class of MyApp, I can use constructor-injection then, any problem?
  3. Any code smell here? Is it ok to move IMyService into project MyApp to make another inversion?
  4. To make MyApp dependent on an interface assembly (don't need extra build every time changing the implementation), how can I achieve this? Maybe like this?
  IService      IApi  
  /      \      /  \            
MyApp    MyCommon   MyAPI

Or this (can those interfaces assembly be merged)? Any suggestion would be helpful, cheers.

          ICommon  
      /      |     \            
MyApp    MyCommon   MyAPI

Edited:
Sorry, I didn't elaborate Q4 well. My intent of all those questions is to decouple the projects.

When the dependency goes like (currently)
MyApp -> MyCommon (IService) -> MyAPI (IMyAPI)
when I change the implementation in MyCommon or MyAPI, I have to rebuild and update the output dll files.

MyApp
- MyCommon.dll

MyCommon
- MyAPI.dll

By applying IOC, (I assumed it is not applied now) the hierarchy can be:
MyApp(IService) <- MyCommon(IMyAPI) <- MyAPI
Then I'm free to update the implementation without affecting MyApp.
(Correct me if anything wrong please)

MyApp

MyCommon
- MyApp.dll

MyAPI
- MyCommon.dll

However, there might be MyWebApp, MyUwpApp and MyMobileApp, so I was thinking about the structure like graph1 in Q4. And dependencies (my intent) can be

MyWebApp
- IMyCommon.dll

MyUwpApp
- IMyCommon.dll

MyCommon
- IMyCommon.dll
- IMyAPI.dll

MyAPI
- IMyAPI.dll

To achieve this, I had my Q1 Q2 Q3, which seems to be necessary changes to me.

Q5 (extended from Q2):
By applying Q4 Graph1, if I leave MyIoc.Current.Register<IApiHelper, ApiHelper>(); in MyCommon, does that still requires a dependency with MyAPI, which would be a circular dependency? Or where shall I do the registration for a container?

Was it helpful?

Solution

Multiple layers' DI registration

Three main projects go as below:
MyApp -> MyCommon -> MyAPI

The main question you have to ask yourself is who decides that MyApi should be used as a dependency? Does MyCommon decide this for itself, or does MyApp get to decide what its own MyCommon dependency should use?

Whoever makes gets the authority to make that decision, is also the one who should do the DI container registration. Below are some simple examples of either scenario.

There is no right or wrong here, it's a matter of how you prefer things to be.

  • If you prefer not referencing all projects in the top-level project, use the second approach.
  • If you have several top-level projects, approach 2 can cut down on how much wiring/copypasting you have to do in the top-level projects' DI registration logic, at the cost of not being able to swap out nested dependencies.
  • If you have multiple top-level project and you need MyCommon to be compatible with any valid dependency, e.g. MyApi for MyApp and MyApi2 for MyApp2, use approach 1.
  • ...

If you have no vested interest in one over the other, pick the approach you prefer. It's fairly easy to change later.


Note that I used the .Net Core DI container as an example since I'm most familiar with its syntax, but the principle is the same for any DI container, as far as I'm aware.

I tend to wrap every project's DI registration into an IServiceCollection extension method so that each project has its own neat little "installer" for all of its internal dependencies.

1. If MyApp decides everything

This means that MyApp needs direct project references to both MyCommon and MyApi

// In MyApi

public static IServiceCollection UseMyApiServices(this IServiceCollection services)
{
    services.AddScoped<IMyApiService, MyApiService>();

    return services;
}

// In MyCommon

    public static IServiceCollection UseMyCommonServices(this IServiceCollection services)
{
    services.AddScoped<IMyCommonService, MyCommonService>();

    return services;
}

// In MyApp

    public static IServiceCollection UseMyAppServices(this IServiceCollection services)
{
    services.AddScoped<IMyAppService, MyAppService>();

    return services;
}

// In MyApp.Startup

services
    .UseMyApiServices()
    .UseMyCommonServices()
    .UseMyAppServices();

2. If MyCommon decides its own dependencies

The benefit here is that MyApp doesn't even need any dependency on MyApi, which some people prefer because it means the top-level application doesn't need to aggregate every single project directly, which makes it harder for developers to accidentally use layer dependencies where they shouldn't.

// In MyApi

public static IServiceCollection UseMyApiServices(this IServiceCollection services)
{
    services.AddScoped<IMyApiService, MyApiService>();

    return services;
}

// In MyCommon

    public static IServiceCollection UseMyCommonServices(this IServiceCollection services)
{
    services.AddScoped<IMyCommonService, MyCommonService>();

    services.UseMyApiServices();

    return services;
}

// In MyApp

    public static IServiceCollection UseMyAppServices(this IServiceCollection services)
{
    services.AddScoped<IMyAppService, MyAppService>();

    return services;
}

// In MyApp.Startup

services
    .UseMyCommonServices()  <-- this internally calls .UseMyApiServices()
    .UseMyAppServices();

Your questions

Is it ok to move IApiHelper into project MyCommon, so the dependency will be an inversion.

There's no problem doing this. Inverted dependencies are perfectly fine to use.

Do I need an individual project to contain all the interfaces for better structure if there are heaps?

The main use case for inverted dependencies is the ability to easily swap out one implementation for the other, but this could also be done by having a separate interface project that both the implementations and the consumer rely on.

In essence, the main difference between the two approaches is whether that "interface project" lives on its own or whether it's part of the consumer (at which point we call it an inverted dependency).

If you have tons of inverted dependencies and don't want to stack them all in the consumer; maybe consider doing separate interface projects, but this is a matter of how you like to organize your codebase, there is no universal right/wrong here.

Is it ok to separate the registration into different classes? If I register MyService in assembly class of MyApp, I can use constructor-injection then, any problem?

Dependency registration logic is logic, and sufficiently complex or bulky logic should generally be split into more manageable chunks. The same approach you use for "real" code applies for dependency registration code.

Any code smell here? Is it ok to move IMyService into project MyApp to make another inversion?

I'd suggest not doing this, because the name ("common") implies reuse, and you can't have inverted dependencies if you have multiple consumers of your common layer, because the consumers would each define their own interface and then you end up with conflicts/duplicates/OCP violations in your common layer.

To make MyApp dependent on an interface assembly (don't need extra build every time changing the implementation), how can I achieve this? Maybe like this?

The main advice here is that I wouldn't bundle all the interfaces (MyApi, MyCommon) in the same interface project, but keep interface projects at least as separate as the projects they represent. It's okay to further subdivide the interface projects (e.g. per module of your common project), but that's up to you whether you need it or not.

Then I'm free to update the implementation without affecting MyApp.
(Correct me if anything wrong please)

Regardless of whether the dependencies are inverted or not, if only the implementation changes and the interface is unchanged, then you never affect the code of MyApp.

Regardless of whether the dependencies are inverted or not, if the interface changes, you affect MyApp, assuming that it has code that interacts with the parts of the interface that have changes to them.

The change process is the same regardless of whether the dependencies are inverted or not.

(don't need extra build every time changing the implementation)

It's correct that using inverted dependencies can in some cases skip a build step, but this is not a driving force behind any architectural decision.

For developer-local builds, the extra step isn't a particularly cumbersome issue and your IDE should figure out the necessary builds anyway.
For proper builds (i.e. for release), you tend to run them on an clean agent anyway, so there is no reliance on pre-existing artifacts, and you'd be building everything from scratch regardless.

OTHER TIPS

Is it ok to move IApiHelper into project MyCommon, so the dependency will be an inversion.

This is fine in some cases. But it means that everything that depend on MyApi also depend on MyCommon. What is the intent?

  • Is MyAPI intended to be used by different projects/applications? If so doing this may not be a good idea.
  • Is MyAPI intended to be replaced by another implementation? If so, this would be fine.
  • If neither is the case, why have different projects? One possible reason might be if they use different languages.

Do I need an individual project to contain all the interfaces for better structure if there are heaps?

This would be most useful if you have multiple applications that might use different, implementations of IApi. This would provide the most flexibility, but requires more projects, and that also has downsides.

Is it ok to separate the registration into different classes?

Yes. I have used a convention where all registrations where in *Module classes that represented some kind of context.

Is it ok to move IMyService into project MyApp to make another inversion?

That would also be fine. But the same reasoning as for MyApi applies.

To make MyApp dependent on an interface assembly (don't need extra build every time changing the implementation), how can I achieve this? Maybe like this?

Either is fine, both alternatives have advantages and disadvantages. It is a balance, too granular projects and dependencies can be difficult to understand and work with. Too coarse projects reduces flexibility.

#1 and #3 are ok as soon as the intent is to provide with an API or framework (MyCommon) for others to build Apps or Services.

For example, this is somewhat but we find in Java and logging APIs. There's an API lib which implements the logging business logic but, instead of providing with a closed implementation, the API offers interfaces for us to make our own logging lib which, usually, enhance the logging API by adding features.

log-api (logic and ports) < --- log-imp (adapters)

I'm not familiar with .Net stack but If your IoC allows you to implement your dependency resolvers, then #2 sounds good. Any DI based on explicit (no framework magic) constructor-injection can't we wrong. Of course, these assembly classes seems out of the place in our source code but located and encapsulated in the right place they are harmless.

Moreover, as Java developer, I use Spring Framework most of the time. Spring's IoC is pure magic oriented DI. Its magic allows developers to declare dependencies between elements without providing the due interface for the injection. For example, we can inject private attributes without setters or constructors. This magic-oriented programming locks the design to the framework to a point that you can not implement unit tests without Spring Test framework or other libs also magic-oriented. In many sense, that's way more code smell than your hypothetic case because Spring is pushing developers to make non-testable code out of the Spring ecosystem.

Regardless of #4, unsure about what the lines describe (dependency or implementation). Assuming it's a dependency, I would go for the one that makes explicit the type of dependency. If anything can be ICommon then I don't know what the hell I have injected in MyApp or MyAPI. If that's fine, then graph 2. If I want to make explicit that MyApp depends on IService, then graph #1 seems the way to go.

Licensed under: CC-BY-SA with attribution
scroll top