DDD - Поведение с необязательным состоянием: Service / Value Object?
Следуя методам 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
}
Как видите, .NET AesCryptoServiceProvider
с состоянием - он принимает ключ как свойство. Но, как я понял, услуги не должны быть с состоянием.
- Является ли ключ, являющийся свойством (а не, скажем, параметром метода), главной ошибкой здесь?
- Как бы вы реализовали класс DDD?
- В некоторых ситуациях инициализация провайдера с заданным ключом кажется полезной и эффективной (если этот ключ нужно много использовать). Есть ли основания для отслеживания состояния или альтернативы?
Я полагаю, что мы могли бы просто создавать новый поставщик при каждом вызове метода, но это кажется очень расточительным. Мы могли бы реализовать кэширование, чтобы уменьшить потери, но тогда все это начинает ощущаться чрезмерно напряженным.
Еще одна альтернатива, которую я предложил, - это создать Aes256CbcCryptorFactory
вместо. Фабрика CreateCryptor(byte[] key)
возвращает объект значения Aes256CbcCryptor
это на самом деле с состоянием. Сервис-потребитель теперь может, по крайней мере, удерживать этот объект в области действия одного из своих методов, если ему необходимо выполнить несколько вызовов криптографии.
С другой стороны, такая потребляющая служба по-прежнему не может хранить объект значения в одном из своих свойств, так как это сделает эту службу сохраняющей состояние.
- Видя, что есть некоторые преимущества, это то, что сделано? Тип поведения кажется очень полезным для объекта значения, но, по крайней мере, у нас может быть какое-то временное состояние.
1 ответ
Я хотел бы пойти с чем-то вроде этого:
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");
}