Refactor Big Blob Factory with SimpleInjector

本文关键字:with SimpleInjector Factory Blob Big Refactor | 更新日期: 2023-09-27 18:03:08

情况

我最近将我的主要Big Blob域类型重构为它的各个方面的facade。到目前为止,facade和各个方面服务都工作得很好,但是创建整个东西并将所有东西连接在一起是在Big Blob Factory中用Poor Man的依赖注入完成的。手工把所有的东西放在一起真的很烦人,所以我觉得我已经把一个大Blob域类型换成了一个大博客域类型的工厂:-/.

我试图遵循坚实的原则,到目前为止,域与我的视图层的连接真的工作得很好,这要归功于QueryHandler和CommandHandler抽象和我最喜欢的依赖注入容器(SimpleInjector来拯救)。

现在我想知道如何使用SimpleInjector来完成我的大型工厂手工完成的工作。

一些示例代码来说明我的情况

请注意,实际的IUser是由其他地方的单独工厂创建的,并作为Command/Query参数对象的属性提交,因此在我的书中,它不被视为服务(例如由DI容器提交),而是作为数据。

在重构之前:

class BigDomainBlob : IDomainService
{
    public BigDomainBlob(Config config)
    {
        // Set up all the big blog business with the help of the
        // given configuration ...
    }
    // Lots of code interleaving the various aspects of the domain
}
interface IUser : System.Security.Principal.IPrincipal
{
    // Session information, ip address, etc.
}
class BigDomainBlobFactory : IFactory<IUser, IDomainService>
{
    // To this point the DI container can help me autowire the
    // factory's dependencies
    public BigDomainBlobFactory(/* ... */)
    {
        // Injecting persistence strategies and client specific
        // plugins to customize the service creation
    }
    public IDomainService Create(IUser user)
    {
        return new BigDomainBlob(new BigDomainBlob.Config
        {
            // Plenty of configuration code to tailor
            // the service to the needs of the given user
        });
    }
}

重构后:

class NeatDomainFacade : IDomainService
{
    private readonly IDataHandlingService service1;
    private readonly IMetaDataService service2;
    public NeatDomainFacade(
        IDataHandlingService service1,
        IMetaDataService service2)
    {
        this.service1 = service1;
        this.service2 = service2;
    }
    // Forwarding the IDomainService functionality
    // to the different aspects of the domain
}
class NeatDomainFacadeFactory : IFactory<IUser, IDomainService>
{
    // To this point the DI container can still help me autowire the
    // factory's dependencies
    public NeatDomainFacadeFactory(/* ... */)
    {
        // Injecting persistence strategies and client specific
        // plugins to customize the service creation
    }
    // I still have to tailor every aspect of the domain to the
    // specific needs of a user by hand so the factory is going nuts
    public IDomainService Create(IUser user) 
    {
        // From here on I'm on my own hand wiring everything together ...
        // ...
        // Lots of code to set up user role, tailor SQL queries, etc.
        // ...
        // Handwiring my aspect dependencies and handing them in
        // to my fresh domain service
        var handWiredServiceForDataHandling = new Internal.DataService(
            someServiceDependency,
            anotherServiceDependency,
            new Internal.DataService.Config
            {
                SomeUserSpecificDbQueryString = "..."
            });
        var handWiredServiceThatAlsoUsesDataHandling = new Internal.MetaDataService(
            handWiredServiceForDataHandling,
            yetAnotherServiceDependency,
            new Internal.MetaDataService.Config
            {
                CustomizedMetaDataRetrievalString = "..."
            });
        // Even more code to hand wire and configure other dependencies ...
        // Finally handing in the service dependencies and creating the domain service
        return NeatDomainFacade(
            handWiredServiceForDataHandling, 
            handWiredServiceThatAlsoUsesDataHandling);
    }
}

所以你看:所有不同的方面不仅需要其他服务,还需要一些数据配置来为用户定制他们的行为,所以所有这些都必须在用户信息可用的阶段进行配置-这意味着我不能在启动时配置它们。我以为我现在已经了解了依赖注入的方法,但我只是不明白如何在自动布线的帮助下实现这一点。

我成功地注入和使用工厂在我的QueryHandler s/CommandHandler s中创建域服务,就像我说的那样:应用程序体系结构的其余部分似乎工作得很好-我几乎不必触及组合根,只需将依赖项放入构造函数并完成。如果我能解决这个pita域服务创建:-)

帮助非常感谢!

<标题>编辑:

我做了steven在他的回答中建议的重构,但现在我面临着生活方式的问题。在手工连接时,我为IDomainService的每个实例创建了每个依赖项,并将它们重用为交叉依赖项。一些代码:

public IDomainService Create(IUser user)
{
    // All dependencies are created exactly once and are
    // being reused throughout this method
    var serviceA = new ServiceA();
    var serviceB = new ServiceB(serviceA);
    var serviceC = new ServiceC(serviceA, serviceB)
    return new DomainService(serviceA, serviceB, serviceC);
}

对于默认的Lifestyle.Transient,情况不再是这样了,每个服务都会获得请求依赖项的新实例。是否有可能得到SimpleInjector来模拟我的手接线行为?

再次感谢!

<标题> Edit2:

我像这样使用SimpleInjector LifetimeScope:

public class CompositionRoot
{
    public static Container container;
    public sealed class DomainServiceFactory : IFactory<IDomainService>
    {
        public IDomainService Create()
        {
            var container = CompositionRoot.container;
            using (container.BeginLifetimeScope())
            {
                return container.GetInstance<IDomainService>();
            }
        }
    }
    public static void Setup(Container container)
    {
        CompositionRoot.container = container;
        container.Register<IDomainService, NeatDomainFacade>(); 
        container.Register<IFactory<IDomainService>, DomainServiceFactory>();
        container.RegisterLifetimeScope<ServiceA>();
        container.RegisterLifetimeScope<ServiceB>();
        container.RegisterLifetimeScope<ServiceC>();
    }
}

现在我可以请求IFactory<IDomainService>,并且生命周期范围正在为依赖项启动。终于不用手工布线了:-)

Refactor Big Blob Factory with SimpleInjector

我认为这里的问题是你正在使用上下文信息为用户构建对象图。

因此,我建议这样做:

  • 防止通过命令和NeatDomainFacadeFactory.Create方法传入用户信息,而是注入一个IUserContext服务,允许在对象图构建后检索IUser
  • 注入额外的服务,使用IUserContext在运行时检索对象所需的适当信息。

因此,不要为一个特定的用户创建一个服务实例,而是创建一个可以用于任何你正在运行的用户的实例,并且只有当你实际通过对象图发送运行时数据(因此在构造之后)时才根据用户做出决策。

您可能需要一些额外的重构才能使其工作。