Reputation: 1900
We have 2 OpenLdap
servers. One is the latest out of the box version. The other one is an older heavily customized version.
Both implementations really follows a similar logic. Take the connection for example.
Here is the interface
namespace Infrastructure.Interfaces
{
public interface ISessionService
{
LdapConnection GetConnection();
}
}
Each of the implementations will make use of this interface to get a connection.
New Server
namespace Infrastructure.NewLdap.Service
{
public class SessionService : ISessionService
{
LdapConnection GetConnection()
{
.....
}
}
}
Old Server
namespace Infrastructure.OldLdap.Service
{
public class SessionService : ISessionService
{
LdapConnection GetConnection()
{
.....
}
}
}
Each of the implementations are in different Projects. Each project will have a different app.config
with the right credentials etc. Along with this there is a IConfigService
which will have all the credentials etc loaded into it from the app.config
.
namespace Infrastructure.Interfaces
{
public interface IConfigService
{
string ServerAddress { get; }
...
...
}
}
Again each project will have its own implementation of this.
The repositories in each project will be different, because of the way we access data in each of the servers. The old server will be used to query only so we are using it to import users into the new server.
How would I use Simple Injector
to inject these services into the same interface but depending on which repository is used, the right implementation will be pulled in? If that makes sense?
Infrastructure.OldLdap.SomeRepo
Use Interfaces.ISessionService
Registered with OldLdap.Service.SessionService
Infrastructure.NewLdap.SomeOtherRepo
Use Interfaces.ISessionService
Registered with NewLdap.Service.SessionService
Is this even possible, Also does this break the Liskov Substitution Principle
.
Am I better to write each implementation as it's own thing? If so, won't this break the DRY
principle?
Hope this is not to broad, thank you in advance.
Upvotes: 1
Views: 370
Reputation: 172855
Also does this break the Liskov Substitution Principle.
Whether or not you break the LSP depends on how the SessionService
classes behave. You should always ask yourself: "What will happen if I swap the implementations?" If injecting the Oldldap
into the NewRepo
causes the NewRepo
to fail at runtime, you are violating the LSP, which is basically telling you your design is wrong. If on the other hand the NewRepo
keeps functioning correctly, because both the Oldldap
and Newldap
behave contractually the same, you're fine. So does the repository care about its implementation, or do only you care. Note that if the Oldldap
performs too slow for NewRepo
, that is probably not a violation of LSP. In that case only you care (or probably your customer, because of non-functional requirements).
The solution to LSP violations is always quite easy: give each implementation its own interface. Developers tend to have problems with this, because both abstractions seem exact duplicates of each other and making 'a copy' seems to violate DRY. But appearances are deceptive; although they have the same members, they have a different, incompatible, contract.
But in case you are not violating LSP, keeping that single interface is fine (according to the LSP). You can make multiple contextual registrations with Simple Injector 3 using RegisterConditional
as follows:
container.RegisterConditional<ISessionService,
Infrastructure.OldLdap.Service.SessionService>(
c => c.Consumer.ImplementationType.Namespace.Contains("Oldldap"));
container.RegisterConditional<ISessionService,
Infrastructure.NewLdap.Service.SessionService>(
c => c.Consumer.ImplementationType.Namespace.Contains("Newldap"));
Upvotes: 4