简体   繁体   中英

How to model this in DDD and repository pattern

I want to model service like this

public class FooService
{
    public GetById(ISecurityContext context, id)
    {
        //checking context has right to view
        //calling Foo repository to getById
    }

    public Add(ISecurityContext context,Foo fooEntity)
    {
        //checking context has right to add
        //calling Foo repository to add
    }



}

In the above methods i want to pass different type of SecurityContext So what i have did is

Public Interface ISecurityContext
{

}

UsernamePasswordContext : ISecurityContext
{
   public string Username { get; set; }
   public string Password { get;set; }

}

SessionContext : ISecurityContext
{
   public string SessionId {get ; set;}
}

So In my Account Service i have a method

public class AccountService
{
    public Account GetAccountFromSecurityContext(ISecurityContext context)
    {
        if(context is UsernamePasswordContext)
            return GetAccountByUsernamePassword(context.Username,context.Password);
        else if (context is SessionContext)
            return GetAccountBySessionId(context.SessionId);

        // more else if for different type of context
    }


}

In the above code i didnt liked so many if else So i tried introducing polymorphism

So in my ISecurityContext interface i added a GetAccount method which all sub class will implement

Public Interface ISecurityContext
{
     Account GetAccount();
}


UsernamePasswordContext : ISecurityContext
{
   public string Username { get; set; }
   public string Password { get;set; }

   public Account GetAccount()
   {
       //call account service 
       GetAccountByUsernamePassword(this.Username,this.Password);
   }


}

and my account service will become like this

public class AccountService
{
    public Account GetAccountFromSecurityContext(ISecurityContext context)
    {
       context.GetAccount();
    }   

}

But the problem here is that i am calling a service/repository from my UsernamePasswordContext POCO which voilates DDD

So what are other ways i can model this scenario.

I think you're not far off from the solution. In this case, I would inject a factory into your AccountService that would take on the responsibility of the if..then..else . Then, the factory could use one of many possible solutions.

One change I would make right off is I would make your AccountService implement an interface which should make it easier to inject later. Assuming you're using some IOC container, you shouldn't have to worry too much about dependencies because you're letting the container handle all that.

Here are the pieces you already had, with some minor ajustments:

public class Account
{
    //some account information and behavior
}

public interface ISecurityContext
{
}

public class UsernamePasswordContext : ISecurityContext
{
    public string Username { get; set; }
    public string Password { get; set; }
}

public class SessionContext : ISecurityContext
{
    public string SessionId { get; set; }
}

Here's your account service along with it's implementation:

public interface IAccountService
{
    Account GetAccountFromSecurityContext(ISecurityContext securityContext);
}

public class AccountService : IAccountService
{
    readonly IAccountFactory _accountFactory;

    public AccountService(IAccountFactory accountFactory)
    {
        _accountFactory = accountFactory;
    }

    public Account GetAccountFromSecurityContext(ISecurityContext securityContext)
    {
        Account account = _accountFactory.Create(securityContext);
        return account;
    }
}

So, you can see here that I've injected an IAccountFactory that will handle the actual creation (retrieval, whatever) of the Account object. All we care about at this point is that the account gets created/retrieved... we don't care about how.


There are a few ways you can implement a factory like this. One way is to use a type of strategy pattern where you have a list of widgets that know how to resolve an account. Then you just pick the widget (strategy) that matches and execute it. Something similar to this would be a factory that uses an IOC or service locator to resolve a type that has been registered previously in application configuration.

In the way of an example, here's one possible implementation of IAccountFactory using CommonServiceLocator :

public interface IAccountFactory
{
    Account Create(ISecurityContext securityContext);
}

public class ServiceLocatorAccountFactory : IAccountFactory
{
    readonly IServiceLocator _serviceLocator;

    public ServiceLocatorAccountFactory(IServiceLocator serviceLocator)
    {
        _serviceLocator = serviceLocator;
    }

    public Account Create(ISecurityContext securityContext)
    {
        var resolverType = typeof (IAccountResolver<>).MakeGenericType(securityContext.GetType());            
        dynamic resolver = _serviceLocator.GetInstance(resolverType);
        return resolver.Resolve(securityContext);
    }
}

My factory here goes out to the service locator context and grabs whatever resolver matches our security context. Here are a couple examples of possible resolvers:

public interface IAccountResolver<in TSecurityContext> where TSecurityContext : ISecurityContext
{
    Account Resolve(TSecurityContext securityContext);
}

public class UsernamePasswordAccountResolver : IAccountResolver<UsernamePasswordContext>
{
    readonly IRepository _repository;

    public UsernamePasswordAccountResolver(IRepository repository)
    {
        _repository = repository;
    }

    public Account Resolve(UsernamePasswordContext securityContext)
    {
        var account = _repository.GetByUsernameAndPassword(securityContext.Username,
                                                           securityContext.Password);

        return account;
    }
}

public class SessionAccountResolver : IAccountResolver<SessionContext>
{
    public Account Resolve(SessionContext securityContext)
    {
        //get the account using the session information
        return someAccount;
    }
}

The only thing left is to register the resolvers in your IOC container so that they can be found when the service locator tries to resolve them in the factory.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM