Stairway pattern implementation
Asked Answered
P

3

31

I came across "Stairway" pattern description in the "Adaptive code via C#" book and I don't really understand how this is supposed to be implemented:

Stairway pattern (source)

So I have client assembly:

using ServiceInterface;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            // Have to create service implementation somehow
            // Where does ServiceFactory belong?
            ServiceFactory serviceFactory = new ServiceFactory();
            IService service = serviceFactory.CreateService();
            service.Do();
        }
    }
}

Service interface assembly:

namespace Service
{
    public interface IService
    {
        void Do();
    }
}

And service implementation assembly:

using ServiceInterface;

namespace ServiceImplementation
{
    public class PrintService : IService
    {
        public void Do()
        {
            Console.WriteLine("Some work done");
        }
    }
}

And the question is: how to I get an IService object in the Client namespace? Where shall I place actual new PrintService() object creation? This can't be a part of ServiceInterface, because interface assembly doesn't depend on ServiceImplementation. But it also can't be a part of Client or ServiceImplementation because Client should only depend on ServiceInterface.

The only solution I came to is having Application assembly on top of it, which has references to all three (Client, ServiceInterface and ServiceImplementation) and injects IService into Client. Am I missing something?

Pathognomy answered 25/3, 2015 at 15:8 Comment(3)
Are you thinking about using Dependency Injection?Wholewheat
I agree with @AndyDangerGagne, thare are some DI Containers that will do this for you.Bresnahan
@AndyDangerGagne, are you talking about forth assembly referencing all others that will be creating concrete service implementation and passing it into client (not really a client now) ctor, which expects any iservice? I can see that, but It kind of ruins this beautiful staircase diagram : )Pathognomy
R
28

Application entry points should be the composition root, as per Mark Seemann's excellent book on Dependency Injection. Here, the issue is more about Dependency Inversion, whereby both client and implementation should both depend on abstractions.

What that diagram doesn't show, but hopefully other parts of the book make clear, is that the entry point will naturally and necessarily reference everything that is needed to construct whatever are your resolution roots (controllers, services, etc.) But this is the only place that has such knowledge.

Bear in mind that it is sometimes ok for clients to 'own' the interfaces on which they depend: the interface ISecurityService might live in the Controllers assembly, the IUserRepository might live in the ServiceImplementations assembly, and so on. This is impractical when >1 client needs access to the interface, of course.

If you follow SOLID, you will naturally find that Dependency Injection is a necessity, but Inversion of Control containers are less of a priority. I find myself using Pure Dependency Injection (manual construction of resolution roots) more and more often.

Richellericher answered 3/4, 2015 at 20:55 Comment(9)
This should be included in the book in my opinion, I had to research this right after reading about it.Touber
I agree with reggaeguitar. Great book though @Gary McLean Hall, reading it as we speak!Amberambergris
@Amberambergris Thanks! I'll clarify in the 2nd edition ;)Richellericher
@GaryMcLeanHall After having the same issue while trying to build a stairway pattern sample, i found this answer from you pretty quick. Thanks! :)Siltstone
@GaryMcLeanHall Firstly great book! But have to say I was also confused by what's asked here. You mentioned 2nd edition, when is it out? Secondly, just to test I understand your explanation, in case of a web api application where I tend to separate the core api layer and the service layer. In this situation, a controller in the api layer is effectively the root client of any service interface implemented by the service layer. So it's ok (and it has to) for the core api project to depende on both the service interface assembly and service interface implementation assembly?Hoof
@stt106 If I understand you question correctly, then the answer is no. The core API should reference interfaces and not implementations. It is up to the composition root (which is almost always the project containing the entry point of the application) to supply the implementation to the client via some form of dependency injection. I'm writing the 2nd edition at the moment and it will be out in the new year.Richellericher
@GaryMcLeanHall I found this answer for similar reasons to other commenters here; I think I understood the composition root was required too, but still preferred web-search to returning to the book for implementation details when trying to apply it for myself. Also as I recall you wrote that this approach did not necessarily lead to a proliferation of libraries / modules. I was unconvinced (!) and I thought that a little more discussion on that with some more in-depth examples might have been helpful.Infrasonic
@GaryMcLeanHall did you read the Mark Seemann book? Do you reccomended it? thanksIvey
@GaryMcLeanHall Reading the second edition right now... this is really the part of the book that I can’t find proper reasoning. The stairway pattern is still not explained properly. Luckily the answer you gave here clarified the subject. Did you forgot to add this on the 2nd edition?Leaves
M
2

In that case Client project should contain references to both Service and ServiceImplementation. Those references will be used only to create IoC container which will be used be DI. At application start you need to register all interface implementations in IoC container.

If you will implement ServiceImplementation against Service interface and you will code Client based on Service intereface then there will be no dependency on ServiceImplementation.

You can also see how Stairway pattern is implemented in samples for "Adaptive Code via C#":

https://github.com/garymcleanhall/AdaptiveCode/tree/master/Sprints/sample-sprint2-markdown

Mintamintage answered 30/3, 2015 at 20:27 Comment(2)
Yep, I see. But the UML diagram from the book clearly says that Controllers doesn't have a reference to ServiceImplementations. That is what got me confused in the first way.Pathognomy
That's right. So Client has references to all of the assemblies but there is no dependencies between them.Mintamintage
T
1

I would put it in the ServiceFactory. You need some parameter e.g. passed in the factory constructor or retrieved from configuration etc. that determines which IService implementation gets created by the factory.

Tsan answered 25/3, 2015 at 15:28 Comment(2)
And where do you place ServiceFactory? If we're placing it in the ServiceImplementation, we're going to have client code referencing both interface and implementation assemblies. If we're placing it in the interfaces assembly, things get pretty messy making interface and implementation assemblies reference each other (which is impossible if I recall correctly).Pathognomy
I was hinting at dependency injection type solution just as @AndyDangerGagne. The factory needs to know how to instantiate classes that implement the IService interface. If you do not want to have reference to both interface and implementation assemblies in your client assembly, the only solution I see is to have another assembly that takes care of that. You already mentioned this in your question.Tsan

© 2022 - 2024 — McMap. All rights reserved.