-1

遵循 DDD 实践,我在实现小型 AES 加密器/解密器(包装 .NET AesCryptoServiceProvider)时遇到了问题。

public class Aes256CbcCryptor : ISymmetricCryptor
{
    private SymmetricAlgorithm AesProvider { get; set; }

    // Poor man's DI - beside the point
    public Aes256Cbc()
    {
        this.AesProvider = new AesCryptoServiceProvider()
        {
            BlockSize = 128,
            KeySize = 256,
            Mode = CipherMode.CBC,
            Padding = PaddingMode.PKCS7,
            Key = // OH DEAR IT TAKES STATE
        };
    }
    public Aes256Cbc(SymmetricAlgorithm aesProvider)
    {
        this.AesProvider = aesProvider;
    }

    public byte[] Encrypt(byte[] keyBytes, byte[] plaintextBytes)
    {} // TODO
    public byte[] Decrypt(byte[] keyBytes, byte[] ciphertextBytes)
    {} // TODO
}

如您所见,.NETAesCryptoServiceProvider是有状态的——它需要一个键作为属性。但据我了解,服务不应该是有状态的。

  1. 关键是属性(而不是方法参数)是这里的主要问题吗?
  2. 您将如何以 DDD 方式实现该类?
  3. 在某些情况下,使用给定密钥初始化提供程序似乎很有用且有效(如果要经常使用该密钥)。有状态服务是否有理由或替代方案?

我想我们可以在每个方法调用上实例化一个新的 Provider,但这似乎非常浪费。我们可以实施缓存来减少浪费,但整个事情开始感觉过度设计。

我想出的另一种选择是创建一个Aes256CbcCryptorFactory代替。工厂CreateCryptor(byte[] key)返回一个Aes256CbcCryptor实际上是有状态的值对象。如果需要进行多次加密调用,消费服务现在至少可以将此对象保留在其方法之一的范围内。

另一方面,这样的消费服务仍然不能将值对象存储在它的一个属性中,因为这样做会使该服务成为有状态的。

  1. 看到有一些好处,这是做的事情吗?这种行为类型对于值对象来说似乎非常有用,但至少我们可以有一些临时状态。
4

1 回答 1

0

我会用这样的东西:

public class Aes256CbcCryptor : ISymmetricCryptor
{
    private SymmetricAlgorithm AesProvider { get; }

    public Aes256CbcCryptor(Byte[] key)
    {
        // AesCryptoServiceProvider is not a 'volatile' dependency
        // therefore we don't need to inject it.
        this.AesProvider = 
            new AesCryptoServiceProvider()
            {
                ...
                Key = key// This is the real dependency, IMHO
            };
    }
}

然后组合根,使用“穷人的 DI”会这样:

public static sub Main()
{
    var key = SomeConfigSomewhere.GetSetting["key"];

    var cryptor = new Aes256CbcCryptor(key);

    var cipherText = cryptor.Encrypt("P@55w0rd");
}

使用容器会使解析更整洁,但本质上是相同的:

public static sub Main()
{
    arbitraryContainer
        .Register
        .ServiceFor<ISymmetricCryptor>()
        .Using<Aes256CbcCryptor>()
        .DependingOn(SomeConfigSomewhere.GetSetting["key"]);

    var cryptor = arbitraryContainer.Resolve<ISymmetricCryptor>();

    var cipherText = cryptor.Encrypt("P@55w0rd");
}
于 2016-10-19T11:13:48.673 回答