Question

I read everything and its opposite about how to organize its code. Of course, I try to follow the SOLID principles, but since I consider myself quite a beginner, I would need some outside advice on how to put them into practice. This concerns the general architecture of the site which I detail the layers below :

  • Controller (Define a request contract) -> Call a Proxy service
  • Proxy service (Input validation, Caching and proceed to log some informations) -> Call concrete service
  • Concrete service (Manage Business logic) -> Call Repository
  • Repository (Query the database) -> Call Database

So those are the layers of my application! The part I am the least confident about is the Proxy service which is an implementation of the Proxy design pattern. Maybe Proxy service got to much Responsibility.

Also, I know this is maybe off-topic, but I don't understand the thing about Inversion of Dependency. I mean, should I really create an Interface for every Proxy service ? And so each interface will have such a lot of function!

Based on the Dependency Inversion Principle all business logic classes should implement a fine-grained interface in order to allow for multiple implementations. Source

But... I've read that Interface must be as small as possible.

So keep your interfaces small. You don't want an interface to have 30 methods on it, 3 is a much better goal. Source

And on the opposite I've also read that, as service are responsible for logical flow, that's ok if they contain a lot of functionality :

Note that service classes may appear to break the Single Responsibility Principle because they may call multiple classes from different layers and packages in a single method but this is because they model the logical flow of data and as such, this is a correct practice. Source

So finally I'm lost between all those informations and I need someone to push me in the good direction !

Was it helpful?

Solution

Interfaces should be dictated by the consumer of the code. Client code if you will. That is, the part of the code that uses it. In this case - if I'm reading correctly - that's the controller.

The question is then: What interface does the controller need? (Not what interface does the service provide).

Figure out what interface does it need, and create that interface. If that results in an interface per service, it would be because it is necessary.

Why do you want those interfaces? In this case, you want the service and its proxy to share the same interface, so you can initialize the controller with the proxy, and it will work just the same as if you initialized it with the actual service. Same goes for mocks, if you need them for testing.

Who initializes the controller? The composition root. Look it up.

Does the proxy have too much responsibility? Well, break it up. You got to initialize the proxy with an instance of the actual service, right? Well, you could give it another proxy. That way you could have proxies doing caching only, proxies doing validation only, and so on.

Oh, by the way, I haven't said that each class should implement a single interface. If it makes sense that a single class implements multiple interfaces from those that the controller needs, great.


Interface segregation principle is not about making interfaces small just because. It is about providing exactly the interface needed. With no extra stuff. It makes perfect sense that a single class would provide multiple of these interfaces if it can be used in multiple ways. However, a method that takes an object of a given interface... Should have the type of that parameter by an interface that describe exactly what the method uses. No extra stuff. The smaller that interface is, the easier it would be have multiple implementations.

I believe, in fact, that your two quotes (the one on fine-grained interfaces, and the one on interface segregation principle) are saying the same thing.


Services having a lot of functionality does not go against interface segregation. It would only suggest they would need a lot of interfaces, each.

Instead, I would argue for services having a lot of functionality being a smell of broken single responsibility principle.

If your service classes are doing too much, I'd suggest to have your service class have the responsibility of bringing together smaller classes that have more specific and clear responsibilities, in such a way that they provide the intended service.


Have I been saying classes and interfaces? I probably should have been saying types and contracts. Subtle difference. Yet, if we generalize that way, then a type could be a lambda and the contract the signature. Sometimes when you have small classes with specific and clear responsibilities they end up with a single method, it is not disimilar.

What kind of tool would we use specify the logic flow of data without going into the details? Something like composition of generators. Like C# Linq. Something that takes cues from functional programming. Thus, if that is the problem you are running into, I'd suggest to borrow some ideas from there. You are doing something that looks a lot like data-in/data-out anyway.

Edit: Oh, by the way, you co can do your proxies like that too, instead of chaining by the interface.


Recommended: Find a recent recording of the talk "Functional Principles for Object Oriented Development" by Jessica Kerr.

OTHER TIPS

I remember believing in these rules:

I used to believe an interface was always just whatever was public on the implementing class.

Then I believed that Interfaces should always be dictated by the consumer of the code.

Now I believe that which of these is true depends on which side the most abstract code lives.

What convinced me was the idea of a plug-in. When you’re a plug-in you end up being both the implementer and the consumer of interfaces. Yet you don’t have any ownership of either interface. You don’t get to write them. You don’t get to dictate that they change. The core code you’re plugging into gets to dictate this. Why? Because the core must not know that the plug-in even exists. The core is more abstract. When the core changes it may break plug-ins. But plug-ins never break the core. The plug-in depends on the core. But the core does not depend on the plug-in.

Of course not every relationship between code modules is a plug-in. But the fact that plug-in’s exist proves something. That back then, when I believed either of the first two rules were absolutes, I was wrong.

You are trying to apply SOLID principles on the service level. Although they are to some extend general principles, they are not a good fit in your case. SOLID applies to object orientation in a programming language context.

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