2016-10-19 19 views
-1

После практики DDD я столкнулся с проблемой при внедрении небольшого AES-шифрования/дешифратора (обертывание .NET AesCryptoServiceProvider).DDD - поведение с дополнительным состоянием: объект службы/значения?

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 является состоянием - он принимает ключ в качестве свойства. Но, как я понял, службы не должны быть сдержанными.

  1. Является ли ключ являющимся собственностью (а не, скажем, параметром метода), главное, что здесь происходит?
  2. Как бы вы реализовали класс в режиме DDD?
  3. В некоторых ситуациях наличие инициатора с заданным ключом представляется полезным и эффективным (если этот ключ должен использоваться много). Есть ли обоснование для служб с сохранением состояния или альтернатива?

Я предполагаю, что мы могли бы просто создать новый провайдер при каждом вызове метода, но это кажется очень расточительным. Мы могли бы реализовать кэширование, чтобы уменьшить количество отходов, но все это начинает ощущаться чрезмерно.

Другая альтернатива, которую я придумал, заключается в том, чтобы вместо этого создать Aes256CbcCryptorFactory. Заводской номер CreateCryptor(byte[] key) возвращает объект Value Aes256CbcCryptor, который фактически является работоспособным. Потребляющая услуга теперь может, по крайней мере, поддерживать этот объект в рамках одного из его методов, если ему необходимо совершить несколько криптовальных вызовов.

С другой стороны, такая потребительская услуга все еще не может хранить объект Value в одном из своих свойств, поскольку это сделает его работоспособным.

  1. Увидев, что есть , некоторые льготы, это что-то, что делается? Тип поведения кажется очень servicey для объекта Value, но по крайней мере мы можем иметь некоторое временное состояние.
+0

Вы прокомментировали ваш конструктор с «Бедным человеком». Здесь нет DI: вы используете ключевое слово 'new' для создания экземпляра. Возможно, это твоя проблема. Разве вы не должны зависеть от зависимостей «AesCryptoServiceProvider», а затем пусть ваш состав root беспокоится о состоянии? –

+0

@DavidOsborne Я опустил параметризованный конструктор, но добавил его сейчас. Беспараметрический конструктор является «DI» бедного человека, то есть альтернативой автоматической инъекции каркасом. Однако я заинтригован вашей точкой зрения. Имеет смысл, что корневой состав будет знать, какой ключ использовать. Этот корень был бы самой службой, правильно? Но разве мы не должны были бы держать это * (и это) от достижения состояния? – Timo

+0

«ИД« бедняк »DI без контейнера (blog.ploeh.dk/2014/06/10/pure-di/). То, что на самом деле является корнем состава, зависит от реализации. Казалось бы, вы не можете избежать того факта, что ваша выбранная реализация требует состояния.Вы можете продвигать разрешение этого состояния дальше и дальше вверх/наружу, но в какой-то момент вам нужно будет управлять этим состоянием. –

ответ

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("[email protected]"); 
} 

Использование контейнера будет сделать разрешение более аккуратно, но по существу будут такими же:

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

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

    var cipherText = cryptor.Encrypt("[email protected]"); 
} 
+0

Я все еще вижу проблему при продвижении абстрактных слоев. Для этого Cryptor ключ может иметь смысл как зависимость конструктора, поскольку он необходим практически всем его методам. Теперь изображение есть служба CardNumberCryptor, которая имеет Cryptor как зависимость. Это, в свою очередь, используется другой службой, которая, в свою очередь, используется службой RecurringCardPayment. Теперь я не могу создать экземпляр/протестировать методы RecurringCardPayment без ключа, что может иметь значение только для * one * его методов. – Timo

+0

'CardNumberCryptor' должен зависеть от' ISymmetricCryptor'. Основная проблема не в этом. –

+0

Ahh, и 'CardNumberCryptor' либо передан некоторый' ISymmetricCryptor', либо контейнер делает это. С контейнером я предполагаю, что тогда контейнер должен знать, какой ключ использовать, что означает, что контейнер очень сильно привязан к (в этом примере) нашему методу Main() - как в вашем последнем примере. Это нормальное положение дел? Это выглядит так ... сложнее. – Timo

 Смежные вопросы

  • Нет связанных вопросов^_^