Knowledge-based authentication using Domain-driven Design in C#First domain-driven User entity classList<List<string>> vs DataTableRectangle ClassDomain driven designed game to play with city namesDomain Driven Design Model for handling imagesDisplay items from DB using MVC and Domain Driven DesignBusiness Rule DSL for Values in Domain-Driven DesignWeb-app for tracking containersModeling the Aggregates in the following Bounded Context - Domain Driven DesignDesign Web API with Command Handler

Approximately how much travel time was saved by the opening of the Suez Canal in 1869?

Is it unprofessional to ask if a job posting on GlassDoor is real?

Email Account under attack (really) - anything I can do?

How old can references or sources in a thesis be?

How does one intimidate enemies without having the capacity for violence?

Theorems that impeded progress

What would happen to a modern skyscraper if it rains micro blackholes?

How to format long polynomial?

Mathematical cryptic clues

How to find program name(s) of an installed package?

The Clique vs. Independent Set Problem

Can an x86 CPU running in real mode be considered to be basically an 8086 CPU?

Fencing style for blades that can attack from a distance

How is it possible to have an ability score that is less than 3?

How do we improve the relationship with a client software team that performs poorly and is becoming less collaborative?

Why is consensus so controversial in Britain?

How much RAM could one put in a typical 80386 setup?

a relationship between local compactness and closure

How can I make a cone from a cube and view the cube with different angles?

Why are electrically insulating heatsinks so rare? Is it just cost?

Why Is Death Allowed In the Matrix?

Show that if two triangles built on parallel lines, with equal bases have the same perimeter only if they are congruent.

Is it tax fraud for an individual to declare non-taxable revenue as taxable income? (US tax laws)

Why does Kotter return in Welcome Back Kotter?



Knowledge-based authentication using Domain-driven Design in C#


First domain-driven User entity classList<List<string>> vs DataTableRectangle ClassDomain driven designed game to play with city namesDomain Driven Design Model for handling imagesDisplay items from DB using MVC and Domain Driven DesignBusiness Rule DSL for Values in Domain-Driven DesignWeb-app for tracking containersModeling the Aggregates in the following Bounded Context - Domain Driven DesignDesign Web API with Command Handler






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;








15












$begingroup$


As part of an insurance claims system we have created, the claims managers can log incoming telephone calls relating to a claim.



The claims manager must validate the caller by asking a number of 'Data Protection' questions that are generated dynamically from information stored against the claim in a database. I believe this type of security is known as 'knowledge-based authentication'.



Notes about Data Protection Questions:



  • Some questions are mandatory and some are not.

  • All mandatory questions must be answered in order to validate the caller.

  • At least one non-mandatory question must be answered in order to validate the
    caller.

  • Additional non-mandatory questions can remain unanswered.

  • Each question may have multiple correct answers.

Below are before and after screen shots of the Data Protection part of the Add Call view:



Before Data Protection has been validated



After Data Protection has been validated



The current system was written five years ago and we did not attempt to use any design patterns or modern approaches (such as Domain-Driven Design) because we lacked the time and understanding.



We now have the opportunity to re-write this software and would like to follow a Domain-driven approach.



First draft of a DataProtectionQuestion entity class:



public class DataProtectionQuestion

public string Question get; set;
public IEnumerable<string> Answers get; set;
public bool IsRequired get; set;
public string Answer get; set;
public bool IsAnswered => !string.IsNullOrWhiteSpace(Answer);
public bool AnswerIsCorrect => Answers?.Contains(Answer) ?? false;



Questions that arose from this design:



  • Is this an anaemic model?

  • Is the DataProtectionQuestion entity the 'right' place to validate the answer?

  • Should the entity have methods like 'SetAnswer' or 'SetAnswerIsValid'?

  • Should the setters be private and should clients supply data through a constructor?

  • Should an Answer be an entity in its own right with a property for 'IsValid'?

  • How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'? (I realise the UI is not the concern of the Domain, but having the ability to choose these as answers is)

Second draft (in an attempt to answer some of the above):



public class DataProtectionAnswer

public DataProtectionAnswer(string answer, bool isValid)

Answer = answer;
IsValid = isValid;

public string Answer get; private set;
public bool IsValid get; private set;


public class DataProtectionQuestion

public DataProtectionQuestion(string question, bool isRequired, IEnumerable<DataProtectionAnswer> answers)

// validate non-null parameters?
Question = question;
IsRequired = isRequired;
Answers = answers;

public string Question get; private set;
public bool IsRequired get; private set;
public IEnumerable<DataProtectionAnswer> Answers get; private set;
public DataProtectionAnswer SelectedAnswer get; private set;
public bool IsAnswered => SelectedAnswer != null;
public bool AnswerIsCorrect => SelectedAnswer?.IsValid ?? false;
public void SetSelectedAnswer(DataProtectionAnswer answer)

// Should validate that answer is not null and contained in Answers?
SelectedAnswer = answer;




Some answers..leading to more questions?:



  • Q. Should the entity have methods like 'SetAnswer' or
    'SetAnswerIsValid'?

  • A. I have added a 'SetSelectedAnswer' method but I still don't know if this 'feels' right?

  • Q. Should the setters be private and should clients supply data through a constructor?

  • A. I don't know but that's what I've done in draft 2.

  • Q. Should an Answer be an entity in its own right with a property for 'IsValid'?

  • A. As per previous question, this is what I've done but should I have?

  • Q. How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'?

  • A. I can now do this by adding an 'Unanswered' and 'Incorrect Answer' DataProtectionAnswer to the DataProtectionQuestion, but this
    'feels' wrong. Isn't this the responsibility of the Presenter?

As you can probably tell, I'm floundering and really struggling to get my head around how to model this scenario using a DDD approach. Perhaps DDD isn't right for this situation. I don't know and I feel very stupid right now!



Can anyone please offer some guidance / suggestions on a way forward / better approach?










share|improve this question







New contributor




datahandler is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$


















    15












    $begingroup$


    As part of an insurance claims system we have created, the claims managers can log incoming telephone calls relating to a claim.



    The claims manager must validate the caller by asking a number of 'Data Protection' questions that are generated dynamically from information stored against the claim in a database. I believe this type of security is known as 'knowledge-based authentication'.



    Notes about Data Protection Questions:



    • Some questions are mandatory and some are not.

    • All mandatory questions must be answered in order to validate the caller.

    • At least one non-mandatory question must be answered in order to validate the
      caller.

    • Additional non-mandatory questions can remain unanswered.

    • Each question may have multiple correct answers.

    Below are before and after screen shots of the Data Protection part of the Add Call view:



    Before Data Protection has been validated



    After Data Protection has been validated



    The current system was written five years ago and we did not attempt to use any design patterns or modern approaches (such as Domain-Driven Design) because we lacked the time and understanding.



    We now have the opportunity to re-write this software and would like to follow a Domain-driven approach.



    First draft of a DataProtectionQuestion entity class:



    public class DataProtectionQuestion

    public string Question get; set;
    public IEnumerable<string> Answers get; set;
    public bool IsRequired get; set;
    public string Answer get; set;
    public bool IsAnswered => !string.IsNullOrWhiteSpace(Answer);
    public bool AnswerIsCorrect => Answers?.Contains(Answer) ?? false;



    Questions that arose from this design:



    • Is this an anaemic model?

    • Is the DataProtectionQuestion entity the 'right' place to validate the answer?

    • Should the entity have methods like 'SetAnswer' or 'SetAnswerIsValid'?

    • Should the setters be private and should clients supply data through a constructor?

    • Should an Answer be an entity in its own right with a property for 'IsValid'?

    • How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'? (I realise the UI is not the concern of the Domain, but having the ability to choose these as answers is)

    Second draft (in an attempt to answer some of the above):



    public class DataProtectionAnswer

    public DataProtectionAnswer(string answer, bool isValid)

    Answer = answer;
    IsValid = isValid;

    public string Answer get; private set;
    public bool IsValid get; private set;


    public class DataProtectionQuestion

    public DataProtectionQuestion(string question, bool isRequired, IEnumerable<DataProtectionAnswer> answers)

    // validate non-null parameters?
    Question = question;
    IsRequired = isRequired;
    Answers = answers;

    public string Question get; private set;
    public bool IsRequired get; private set;
    public IEnumerable<DataProtectionAnswer> Answers get; private set;
    public DataProtectionAnswer SelectedAnswer get; private set;
    public bool IsAnswered => SelectedAnswer != null;
    public bool AnswerIsCorrect => SelectedAnswer?.IsValid ?? false;
    public void SetSelectedAnswer(DataProtectionAnswer answer)

    // Should validate that answer is not null and contained in Answers?
    SelectedAnswer = answer;




    Some answers..leading to more questions?:



    • Q. Should the entity have methods like 'SetAnswer' or
      'SetAnswerIsValid'?

    • A. I have added a 'SetSelectedAnswer' method but I still don't know if this 'feels' right?

    • Q. Should the setters be private and should clients supply data through a constructor?

    • A. I don't know but that's what I've done in draft 2.

    • Q. Should an Answer be an entity in its own right with a property for 'IsValid'?

    • A. As per previous question, this is what I've done but should I have?

    • Q. How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'?

    • A. I can now do this by adding an 'Unanswered' and 'Incorrect Answer' DataProtectionAnswer to the DataProtectionQuestion, but this
      'feels' wrong. Isn't this the responsibility of the Presenter?

    As you can probably tell, I'm floundering and really struggling to get my head around how to model this scenario using a DDD approach. Perhaps DDD isn't right for this situation. I don't know and I feel very stupid right now!



    Can anyone please offer some guidance / suggestions on a way forward / better approach?










    share|improve this question







    New contributor




    datahandler is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.







    $endgroup$














      15












      15








      15


      4



      $begingroup$


      As part of an insurance claims system we have created, the claims managers can log incoming telephone calls relating to a claim.



      The claims manager must validate the caller by asking a number of 'Data Protection' questions that are generated dynamically from information stored against the claim in a database. I believe this type of security is known as 'knowledge-based authentication'.



      Notes about Data Protection Questions:



      • Some questions are mandatory and some are not.

      • All mandatory questions must be answered in order to validate the caller.

      • At least one non-mandatory question must be answered in order to validate the
        caller.

      • Additional non-mandatory questions can remain unanswered.

      • Each question may have multiple correct answers.

      Below are before and after screen shots of the Data Protection part of the Add Call view:



      Before Data Protection has been validated



      After Data Protection has been validated



      The current system was written five years ago and we did not attempt to use any design patterns or modern approaches (such as Domain-Driven Design) because we lacked the time and understanding.



      We now have the opportunity to re-write this software and would like to follow a Domain-driven approach.



      First draft of a DataProtectionQuestion entity class:



      public class DataProtectionQuestion

      public string Question get; set;
      public IEnumerable<string> Answers get; set;
      public bool IsRequired get; set;
      public string Answer get; set;
      public bool IsAnswered => !string.IsNullOrWhiteSpace(Answer);
      public bool AnswerIsCorrect => Answers?.Contains(Answer) ?? false;



      Questions that arose from this design:



      • Is this an anaemic model?

      • Is the DataProtectionQuestion entity the 'right' place to validate the answer?

      • Should the entity have methods like 'SetAnswer' or 'SetAnswerIsValid'?

      • Should the setters be private and should clients supply data through a constructor?

      • Should an Answer be an entity in its own right with a property for 'IsValid'?

      • How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'? (I realise the UI is not the concern of the Domain, but having the ability to choose these as answers is)

      Second draft (in an attempt to answer some of the above):



      public class DataProtectionAnswer

      public DataProtectionAnswer(string answer, bool isValid)

      Answer = answer;
      IsValid = isValid;

      public string Answer get; private set;
      public bool IsValid get; private set;


      public class DataProtectionQuestion

      public DataProtectionQuestion(string question, bool isRequired, IEnumerable<DataProtectionAnswer> answers)

      // validate non-null parameters?
      Question = question;
      IsRequired = isRequired;
      Answers = answers;

      public string Question get; private set;
      public bool IsRequired get; private set;
      public IEnumerable<DataProtectionAnswer> Answers get; private set;
      public DataProtectionAnswer SelectedAnswer get; private set;
      public bool IsAnswered => SelectedAnswer != null;
      public bool AnswerIsCorrect => SelectedAnswer?.IsValid ?? false;
      public void SetSelectedAnswer(DataProtectionAnswer answer)

      // Should validate that answer is not null and contained in Answers?
      SelectedAnswer = answer;




      Some answers..leading to more questions?:



      • Q. Should the entity have methods like 'SetAnswer' or
        'SetAnswerIsValid'?

      • A. I have added a 'SetSelectedAnswer' method but I still don't know if this 'feels' right?

      • Q. Should the setters be private and should clients supply data through a constructor?

      • A. I don't know but that's what I've done in draft 2.

      • Q. Should an Answer be an entity in its own right with a property for 'IsValid'?

      • A. As per previous question, this is what I've done but should I have?

      • Q. How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'?

      • A. I can now do this by adding an 'Unanswered' and 'Incorrect Answer' DataProtectionAnswer to the DataProtectionQuestion, but this
        'feels' wrong. Isn't this the responsibility of the Presenter?

      As you can probably tell, I'm floundering and really struggling to get my head around how to model this scenario using a DDD approach. Perhaps DDD isn't right for this situation. I don't know and I feel very stupid right now!



      Can anyone please offer some guidance / suggestions on a way forward / better approach?










      share|improve this question







      New contributor




      datahandler is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.







      $endgroup$




      As part of an insurance claims system we have created, the claims managers can log incoming telephone calls relating to a claim.



      The claims manager must validate the caller by asking a number of 'Data Protection' questions that are generated dynamically from information stored against the claim in a database. I believe this type of security is known as 'knowledge-based authentication'.



      Notes about Data Protection Questions:



      • Some questions are mandatory and some are not.

      • All mandatory questions must be answered in order to validate the caller.

      • At least one non-mandatory question must be answered in order to validate the
        caller.

      • Additional non-mandatory questions can remain unanswered.

      • Each question may have multiple correct answers.

      Below are before and after screen shots of the Data Protection part of the Add Call view:



      Before Data Protection has been validated



      After Data Protection has been validated



      The current system was written five years ago and we did not attempt to use any design patterns or modern approaches (such as Domain-Driven Design) because we lacked the time and understanding.



      We now have the opportunity to re-write this software and would like to follow a Domain-driven approach.



      First draft of a DataProtectionQuestion entity class:



      public class DataProtectionQuestion

      public string Question get; set;
      public IEnumerable<string> Answers get; set;
      public bool IsRequired get; set;
      public string Answer get; set;
      public bool IsAnswered => !string.IsNullOrWhiteSpace(Answer);
      public bool AnswerIsCorrect => Answers?.Contains(Answer) ?? false;



      Questions that arose from this design:



      • Is this an anaemic model?

      • Is the DataProtectionQuestion entity the 'right' place to validate the answer?

      • Should the entity have methods like 'SetAnswer' or 'SetAnswerIsValid'?

      • Should the setters be private and should clients supply data through a constructor?

      • Should an Answer be an entity in its own right with a property for 'IsValid'?

      • How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'? (I realise the UI is not the concern of the Domain, but having the ability to choose these as answers is)

      Second draft (in an attempt to answer some of the above):



      public class DataProtectionAnswer

      public DataProtectionAnswer(string answer, bool isValid)

      Answer = answer;
      IsValid = isValid;

      public string Answer get; private set;
      public bool IsValid get; private set;


      public class DataProtectionQuestion

      public DataProtectionQuestion(string question, bool isRequired, IEnumerable<DataProtectionAnswer> answers)

      // validate non-null parameters?
      Question = question;
      IsRequired = isRequired;
      Answers = answers;

      public string Question get; private set;
      public bool IsRequired get; private set;
      public IEnumerable<DataProtectionAnswer> Answers get; private set;
      public DataProtectionAnswer SelectedAnswer get; private set;
      public bool IsAnswered => SelectedAnswer != null;
      public bool AnswerIsCorrect => SelectedAnswer?.IsValid ?? false;
      public void SetSelectedAnswer(DataProtectionAnswer answer)

      // Should validate that answer is not null and contained in Answers?
      SelectedAnswer = answer;




      Some answers..leading to more questions?:



      • Q. Should the entity have methods like 'SetAnswer' or
        'SetAnswerIsValid'?

      • A. I have added a 'SetSelectedAnswer' method but I still don't know if this 'feels' right?

      • Q. Should the setters be private and should clients supply data through a constructor?

      • A. I don't know but that's what I've done in draft 2.

      • Q. Should an Answer be an entity in its own right with a property for 'IsValid'?

      • A. As per previous question, this is what I've done but should I have?

      • Q. How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'?

      • A. I can now do this by adding an 'Unanswered' and 'Incorrect Answer' DataProtectionAnswer to the DataProtectionQuestion, but this
        'feels' wrong. Isn't this the responsibility of the Presenter?

      As you can probably tell, I'm floundering and really struggling to get my head around how to model this scenario using a DDD approach. Perhaps DDD isn't right for this situation. I don't know and I feel very stupid right now!



      Can anyone please offer some guidance / suggestions on a way forward / better approach?







      c# ddd






      share|improve this question







      New contributor




      datahandler is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      share|improve this question







      New contributor




      datahandler is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      share|improve this question




      share|improve this question






      New contributor




      datahandler is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      asked Apr 2 at 15:19









      datahandlerdatahandler

      814




      814




      New contributor




      datahandler is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





      New contributor





      datahandler is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






      datahandler is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.




















          1 Answer
          1






          active

          oldest

          votes


















          17












          $begingroup$

          To start with, you're not doing DDD here.



          DDD (Domain-Driven Design / Development) is based around the idea that we start with the domain. We don't touch code yet—we develop the domain models on-paper (or whiteboard, whatever is preferred). Once that is done, we build the code as closely to the domain as possible. The point of DDD is that the code should mirror the domain design.



          Before we get going, I highly, highly, highly recommend this book, by Scott Wlaschin, a prominent F# developer who brings DDD into a very easy-to-understand view (the examples are F#, but they apply to C# as well): Domain Modeling made Functional



          DDD is about:




          1. Define the domain, the inputs, and the outputs. That is, as a user of the system, what does the domain need to do. Here it sounds like we have part of the domain defined:




            As part of an insurance claims system we have created, the claims managers can log incoming telephone calls relating to a claim.



            The claims manager must validate the caller by asking a number of 'Data Protection' questions that are generated dynamically from information stored against the claim in a database. I believe this type of security is known as 'knowledge-based authentication'.



            Notes about Data Protection Questions:



            • Some questions are mandatory and some are not.

            • All mandatory questions must be answered in order to validate the caller.

            • At least one non-mandatory question must be answered in order to validate the
              caller.

            • Additional non-mandatory questions can remain unanswered.

            • Each question may have multiple correct answers.




          2. From there, we define our types. Generally, I do DDD with F#, but it's just as applicable to C#. We model the physical domain, so here we're not modeling the questions, we're modeling the validation. That is: the user must answer various questions and prove they are knowledgeable on the claim.



            This is the root of our domain model: we need to validate some information. You have mixed multiple pieces here, so we're going to separate them a bit.



          3. After building the types, we build the work. That is, the functions. We build the types as just data-structures, then we build the functions next to encapsulate the domain rules.


          So, you've defined the domain (at least, as far as I see it) via the quoted-blurb, so what I want to do is move that into some types.



          To start with, we'll define a DataProtectionResponse (we're going to use the exact language from the domain model, the purpose of DDD is to translate the human-language into code).



          class DataProtectionResponse 
          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;



          Now, we need to come up with a model for DataProtectionQuestion:



          class DataProtectionQuestion 
          public string Question get; set;
          public bool Required get; set;



          As you see, we are ONLY modeling two components of the question: the actual question, and whether or not it's required. The questions themselves are a different part of the domain, they're generated as a question, and using this is how we get into building a flexible model. We can now take these same questions somewhere else, and use them as a whole other tool, assuming it needs to interact with our current domain.



          Next, we have ValidQuestionAnswer. This is going to be the answer that are valid for this particular claim:



          class ValidQuestionAnswer 
          public Response Response get; set;



          We made this a class as we absolutely want to consider a situation where an answer might have more data to it.



          Finally, the Response. You might say, "Der Kommissar, why does that need to be a class, it's always a string?" Again, we might need to add more to this model, including functionality, so we do that by using a class.



          class Response 
          public string Value get; set;



          So now, our domain will consume an IEnumerable<DataProtectionResponse>, but not directly.



          class DataProtection 
          public IEnumerable<DataProtectionResponse> Questions get; set;



          Why another class? Well, let's start talking functionality.



          First and foremost, the primary component of our design is that DataProtection must validate. For this to work, we need a IsValid function or property there:



          public bool IsValid => Questions.All(x => x.IsSufficient);


          Alright, so we have some concepts now. We have a IsValid that indicates if our DataProtection is valid or not, and we have decided that all of the questions must be sufficiently answered.



          Next, we need to prove that a question is sufficiently answered.



          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));


          Again, we are going to encode our actual logic: this DataProtectionResponse is sufficient if any of the ValidQuestionAnswers are acceptable with the question and response.



          Next, how do we prove they're acceptable?



          Well, the first rule is that if it's not required and there is no response, then it's valid:



          if (!question.Required && response?.IsEmpty ?? false == false)

          return true;



          And of course, Response.IsEmpty:



          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);


          Otherwise, we want to prove that this response and the provided response are acceptable:



          return response.Satisfies(Response);


          And this is why we made it a class right-off-the-bat: we might have more logic that goes into Satisfies that might do heuristic analysis. I.e. if you provide an address, the logic might say 123 Main St. and 123 Main St and 123 Main Street are all the same.



          public bool Acceptable(DataProtectionQuestion question, Response response)

          if (!question.Required && response?.IsEmpty ?? true)

          return true;


          return response.Satisfies(Response);



          Next, our Response.Satisfies:



          public bool Satisfies(Response response) => Value == response.Value;


          And viola, we're done. We've encoded the entire domain, concisely, and using the actual terms the domain considers. Only 37 lines of code:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool Acceptable(DataProtectionQuestion question, Response response)

          if (!question.Required && response?.IsEmpty ?? true)

          return true;


          return response.Satisfies(Response);


          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient);



          We don't have any odd logic, we don't have any conflated values: each model concerns itself with it's own work, no one else's.



          Additionally, when our domain changes now (or we have to change the satisfaction logic) we have built the flexibility in-place without needing major infrastructure rewrites. If we need to override a response, we encode that in DataProtectionResponse and modify IsSufficient.



          Finally, you could even shorten Acceptable to a single statement, since the logic is relatively straightforward:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true)
          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient);




          You noted in the comments that I missed the requirement of at least one optional question, and you're absolutely right. So let's talk about another strength of DDD: verifying our code matches the domain.




          At least one non-mandatory question must be answered in order to validate the caller.




          So, looking through our code we see that we can start with DataProtection.IsValid:



          public bool IsValid => Questions.All(x => x.IsSufficient);


          Aha, we already know our problem is here. We make sure all questions are sufficient, but that does not take at least one optional into account. So how do we fix that?



          Well, to start, we'll modify IsValid to support some theoretical work:



          public bool IsValid => Questions.All(x => x.IsSufficient) && Questions.Where(x => !x.IsRequired).Any(x => x.IsAnswered);


          Here, we've decided that if each DataProtectionResponse tells us if it's required or not, and if it's answered or not, we can prove that we have at least one non-required question answered.



          Next, we need to implement those two items. Both are trivial, and actually help with our other code:



          public bool IsRequired => Question.Required;
          public bool IsAnswered => ValidQuestionAnswers.Any(x => x.AnsweredBy(Response));


          Now we have another method to implement: ValidQuestionAnswer.AnsweredBy(Response):



          public bool AnsweredBy(Response response) => response.Satisfies(Response);


          You'll notice we've repeated one bit of code: response.Satisfies(Response) is in both AnsweredBy, and Acceptable, let's change that:



          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true) || AnsweredBy(Response);


          Once again, our contract is now satisfied, and we ONLY go at-most one-level-deep in each model from a parent model. (That is, we could have done !x.Question.Required instead of !x.IsRequired, but it's not the responsibility of DataProtection to know what makes a response required or not.)



          So, 36 lines of code to build our new requirements:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool AnsweredBy(Response response) => response.Satisfies(Response);

          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true)
          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;

          public bool IsRequired => Question.Required;
          public bool IsAnswered => ValidQuestionAnswers.Any(x => x.AnsweredBy(Response));
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient) && Questions.Where(x => !x.IsRequired).Any(x => x.IsAnswered);




          Finally, you asked a few questions about your implementation: as you see, I ignored them, purposefully. With DDD, those questions are not things to ask about the implementation but things to ask about the domain. With DDD we do iterations of "design", "implement", "design", "implement"—all the questions you have should go in the design stage, which is where you (and the other domain experts) gather and hash-out the principles of the project. This means, now that you have an implementation, we go back to design and clarify those questions. As the developer, when you see these things you should be creating a working list of potential problem-points, you might find out the domain-experts have considered them, or they may not, so you take your concerns back to them and refine the design. (Again, DDD is an iterative concept.)



          But, suppose I were to answer them:




          Should the entity have methods like 'SetAnswer' or 'SetAnswerIsValid'?




          A: That's actually a design question, do you need to override whether an answer is valid or not? (I briefly touched on that in the first part of the answer.)




          Should the setters be private and should clients supply data through a constructor?




          A: This seems like an implementation question at first glance, but if we reword it things are different: can a client change an answer? If the answer is yes, the proposed design is fine. If not, model for immutability.




          Should an Answer be an entity in its own right with a property for 'IsValid'?




          A: In my humble opinion, yes. Answers aren't a string, they're a concept. Additionally, with a base-class, you can override that for answers that are bool, DateTime, etc. But, again: take it back to the DDD drawing board. The domain model will tell you what needs done.




          How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'?




          A: That's a design question, but my suggestion is to ditch the Valid checkbox and provide a red/green "Needs Answered", "Incorrect Answer", or "Correct Answer" state. Again, do what the domain calls for. You're mixing some concerns here, and with DDD we create a clear separation. When you have a question like this, we go back into the design stage and hash it out. (It might turn out that you must not indicate if an answer is incorrect. Compliance laws are weird.)






          share|improve this answer











          $endgroup$












          • $begingroup$
            Thank you for taking the time to write such a comprehensive response. It is much appreciated. You're absolutely right when you say I'm mixing concerns. I am struggling to get my mindset away from data-driven towards domain-drive design. Your suggested approach to DDD has given me much needed food for thought. One thing I mentioned in my original post that I don't think your answer covers: "At least one non-mandatory question must be answered in order to validate the caller." I believe the code you have suggested allows for all non-mandatory questions to be left unanswered?
            $endgroup$
            – datahandler
            Apr 3 at 8:27










          • $begingroup$
            @datahandler I just edited the answer to address that (I did not change the existing answer, but instead showed how we would validate that the model we built conforms to the DDD specification). Hopefully all of this helps you figure out where you can modify the process to do DDD a little more effectively. :)
            $endgroup$
            – Der Kommissar
            Apr 3 at 13:24










          • $begingroup$
            @DerKommissar Excellent! Thanks again!
            $endgroup$
            – datahandler
            Apr 3 at 14:38











          Your Answer





          StackExchange.ifUsing("editor", function ()
          return StackExchange.using("mathjaxEditing", function ()
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          );
          );
          , "mathjax-editing");

          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "196"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader:
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          ,
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );






          datahandler is a new contributor. Be nice, and check out our Code of Conduct.









          draft saved

          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216727%2fknowledge-based-authentication-using-domain-driven-design-in-c%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          17












          $begingroup$

          To start with, you're not doing DDD here.



          DDD (Domain-Driven Design / Development) is based around the idea that we start with the domain. We don't touch code yet—we develop the domain models on-paper (or whiteboard, whatever is preferred). Once that is done, we build the code as closely to the domain as possible. The point of DDD is that the code should mirror the domain design.



          Before we get going, I highly, highly, highly recommend this book, by Scott Wlaschin, a prominent F# developer who brings DDD into a very easy-to-understand view (the examples are F#, but they apply to C# as well): Domain Modeling made Functional



          DDD is about:




          1. Define the domain, the inputs, and the outputs. That is, as a user of the system, what does the domain need to do. Here it sounds like we have part of the domain defined:




            As part of an insurance claims system we have created, the claims managers can log incoming telephone calls relating to a claim.



            The claims manager must validate the caller by asking a number of 'Data Protection' questions that are generated dynamically from information stored against the claim in a database. I believe this type of security is known as 'knowledge-based authentication'.



            Notes about Data Protection Questions:



            • Some questions are mandatory and some are not.

            • All mandatory questions must be answered in order to validate the caller.

            • At least one non-mandatory question must be answered in order to validate the
              caller.

            • Additional non-mandatory questions can remain unanswered.

            • Each question may have multiple correct answers.




          2. From there, we define our types. Generally, I do DDD with F#, but it's just as applicable to C#. We model the physical domain, so here we're not modeling the questions, we're modeling the validation. That is: the user must answer various questions and prove they are knowledgeable on the claim.



            This is the root of our domain model: we need to validate some information. You have mixed multiple pieces here, so we're going to separate them a bit.



          3. After building the types, we build the work. That is, the functions. We build the types as just data-structures, then we build the functions next to encapsulate the domain rules.


          So, you've defined the domain (at least, as far as I see it) via the quoted-blurb, so what I want to do is move that into some types.



          To start with, we'll define a DataProtectionResponse (we're going to use the exact language from the domain model, the purpose of DDD is to translate the human-language into code).



          class DataProtectionResponse 
          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;



          Now, we need to come up with a model for DataProtectionQuestion:



          class DataProtectionQuestion 
          public string Question get; set;
          public bool Required get; set;



          As you see, we are ONLY modeling two components of the question: the actual question, and whether or not it's required. The questions themselves are a different part of the domain, they're generated as a question, and using this is how we get into building a flexible model. We can now take these same questions somewhere else, and use them as a whole other tool, assuming it needs to interact with our current domain.



          Next, we have ValidQuestionAnswer. This is going to be the answer that are valid for this particular claim:



          class ValidQuestionAnswer 
          public Response Response get; set;



          We made this a class as we absolutely want to consider a situation where an answer might have more data to it.



          Finally, the Response. You might say, "Der Kommissar, why does that need to be a class, it's always a string?" Again, we might need to add more to this model, including functionality, so we do that by using a class.



          class Response 
          public string Value get; set;



          So now, our domain will consume an IEnumerable<DataProtectionResponse>, but not directly.



          class DataProtection 
          public IEnumerable<DataProtectionResponse> Questions get; set;



          Why another class? Well, let's start talking functionality.



          First and foremost, the primary component of our design is that DataProtection must validate. For this to work, we need a IsValid function or property there:



          public bool IsValid => Questions.All(x => x.IsSufficient);


          Alright, so we have some concepts now. We have a IsValid that indicates if our DataProtection is valid or not, and we have decided that all of the questions must be sufficiently answered.



          Next, we need to prove that a question is sufficiently answered.



          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));


          Again, we are going to encode our actual logic: this DataProtectionResponse is sufficient if any of the ValidQuestionAnswers are acceptable with the question and response.



          Next, how do we prove they're acceptable?



          Well, the first rule is that if it's not required and there is no response, then it's valid:



          if (!question.Required && response?.IsEmpty ?? false == false)

          return true;



          And of course, Response.IsEmpty:



          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);


          Otherwise, we want to prove that this response and the provided response are acceptable:



          return response.Satisfies(Response);


          And this is why we made it a class right-off-the-bat: we might have more logic that goes into Satisfies that might do heuristic analysis. I.e. if you provide an address, the logic might say 123 Main St. and 123 Main St and 123 Main Street are all the same.



          public bool Acceptable(DataProtectionQuestion question, Response response)

          if (!question.Required && response?.IsEmpty ?? true)

          return true;


          return response.Satisfies(Response);



          Next, our Response.Satisfies:



          public bool Satisfies(Response response) => Value == response.Value;


          And viola, we're done. We've encoded the entire domain, concisely, and using the actual terms the domain considers. Only 37 lines of code:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool Acceptable(DataProtectionQuestion question, Response response)

          if (!question.Required && response?.IsEmpty ?? true)

          return true;


          return response.Satisfies(Response);


          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient);



          We don't have any odd logic, we don't have any conflated values: each model concerns itself with it's own work, no one else's.



          Additionally, when our domain changes now (or we have to change the satisfaction logic) we have built the flexibility in-place without needing major infrastructure rewrites. If we need to override a response, we encode that in DataProtectionResponse and modify IsSufficient.



          Finally, you could even shorten Acceptable to a single statement, since the logic is relatively straightforward:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true)
          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient);




          You noted in the comments that I missed the requirement of at least one optional question, and you're absolutely right. So let's talk about another strength of DDD: verifying our code matches the domain.




          At least one non-mandatory question must be answered in order to validate the caller.




          So, looking through our code we see that we can start with DataProtection.IsValid:



          public bool IsValid => Questions.All(x => x.IsSufficient);


          Aha, we already know our problem is here. We make sure all questions are sufficient, but that does not take at least one optional into account. So how do we fix that?



          Well, to start, we'll modify IsValid to support some theoretical work:



          public bool IsValid => Questions.All(x => x.IsSufficient) && Questions.Where(x => !x.IsRequired).Any(x => x.IsAnswered);


          Here, we've decided that if each DataProtectionResponse tells us if it's required or not, and if it's answered or not, we can prove that we have at least one non-required question answered.



          Next, we need to implement those two items. Both are trivial, and actually help with our other code:



          public bool IsRequired => Question.Required;
          public bool IsAnswered => ValidQuestionAnswers.Any(x => x.AnsweredBy(Response));


          Now we have another method to implement: ValidQuestionAnswer.AnsweredBy(Response):



          public bool AnsweredBy(Response response) => response.Satisfies(Response);


          You'll notice we've repeated one bit of code: response.Satisfies(Response) is in both AnsweredBy, and Acceptable, let's change that:



          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true) || AnsweredBy(Response);


          Once again, our contract is now satisfied, and we ONLY go at-most one-level-deep in each model from a parent model. (That is, we could have done !x.Question.Required instead of !x.IsRequired, but it's not the responsibility of DataProtection to know what makes a response required or not.)



          So, 36 lines of code to build our new requirements:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool AnsweredBy(Response response) => response.Satisfies(Response);

          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true)
          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;

          public bool IsRequired => Question.Required;
          public bool IsAnswered => ValidQuestionAnswers.Any(x => x.AnsweredBy(Response));
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient) && Questions.Where(x => !x.IsRequired).Any(x => x.IsAnswered);




          Finally, you asked a few questions about your implementation: as you see, I ignored them, purposefully. With DDD, those questions are not things to ask about the implementation but things to ask about the domain. With DDD we do iterations of "design", "implement", "design", "implement"—all the questions you have should go in the design stage, which is where you (and the other domain experts) gather and hash-out the principles of the project. This means, now that you have an implementation, we go back to design and clarify those questions. As the developer, when you see these things you should be creating a working list of potential problem-points, you might find out the domain-experts have considered them, or they may not, so you take your concerns back to them and refine the design. (Again, DDD is an iterative concept.)



          But, suppose I were to answer them:




          Should the entity have methods like 'SetAnswer' or 'SetAnswerIsValid'?




          A: That's actually a design question, do you need to override whether an answer is valid or not? (I briefly touched on that in the first part of the answer.)




          Should the setters be private and should clients supply data through a constructor?




          A: This seems like an implementation question at first glance, but if we reword it things are different: can a client change an answer? If the answer is yes, the proposed design is fine. If not, model for immutability.




          Should an Answer be an entity in its own right with a property for 'IsValid'?




          A: In my humble opinion, yes. Answers aren't a string, they're a concept. Additionally, with a base-class, you can override that for answers that are bool, DateTime, etc. But, again: take it back to the DDD drawing board. The domain model will tell you what needs done.




          How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'?




          A: That's a design question, but my suggestion is to ditch the Valid checkbox and provide a red/green "Needs Answered", "Incorrect Answer", or "Correct Answer" state. Again, do what the domain calls for. You're mixing some concerns here, and with DDD we create a clear separation. When you have a question like this, we go back into the design stage and hash it out. (It might turn out that you must not indicate if an answer is incorrect. Compliance laws are weird.)






          share|improve this answer











          $endgroup$












          • $begingroup$
            Thank you for taking the time to write such a comprehensive response. It is much appreciated. You're absolutely right when you say I'm mixing concerns. I am struggling to get my mindset away from data-driven towards domain-drive design. Your suggested approach to DDD has given me much needed food for thought. One thing I mentioned in my original post that I don't think your answer covers: "At least one non-mandatory question must be answered in order to validate the caller." I believe the code you have suggested allows for all non-mandatory questions to be left unanswered?
            $endgroup$
            – datahandler
            Apr 3 at 8:27










          • $begingroup$
            @datahandler I just edited the answer to address that (I did not change the existing answer, but instead showed how we would validate that the model we built conforms to the DDD specification). Hopefully all of this helps you figure out where you can modify the process to do DDD a little more effectively. :)
            $endgroup$
            – Der Kommissar
            Apr 3 at 13:24










          • $begingroup$
            @DerKommissar Excellent! Thanks again!
            $endgroup$
            – datahandler
            Apr 3 at 14:38















          17












          $begingroup$

          To start with, you're not doing DDD here.



          DDD (Domain-Driven Design / Development) is based around the idea that we start with the domain. We don't touch code yet—we develop the domain models on-paper (or whiteboard, whatever is preferred). Once that is done, we build the code as closely to the domain as possible. The point of DDD is that the code should mirror the domain design.



          Before we get going, I highly, highly, highly recommend this book, by Scott Wlaschin, a prominent F# developer who brings DDD into a very easy-to-understand view (the examples are F#, but they apply to C# as well): Domain Modeling made Functional



          DDD is about:




          1. Define the domain, the inputs, and the outputs. That is, as a user of the system, what does the domain need to do. Here it sounds like we have part of the domain defined:




            As part of an insurance claims system we have created, the claims managers can log incoming telephone calls relating to a claim.



            The claims manager must validate the caller by asking a number of 'Data Protection' questions that are generated dynamically from information stored against the claim in a database. I believe this type of security is known as 'knowledge-based authentication'.



            Notes about Data Protection Questions:



            • Some questions are mandatory and some are not.

            • All mandatory questions must be answered in order to validate the caller.

            • At least one non-mandatory question must be answered in order to validate the
              caller.

            • Additional non-mandatory questions can remain unanswered.

            • Each question may have multiple correct answers.




          2. From there, we define our types. Generally, I do DDD with F#, but it's just as applicable to C#. We model the physical domain, so here we're not modeling the questions, we're modeling the validation. That is: the user must answer various questions and prove they are knowledgeable on the claim.



            This is the root of our domain model: we need to validate some information. You have mixed multiple pieces here, so we're going to separate them a bit.



          3. After building the types, we build the work. That is, the functions. We build the types as just data-structures, then we build the functions next to encapsulate the domain rules.


          So, you've defined the domain (at least, as far as I see it) via the quoted-blurb, so what I want to do is move that into some types.



          To start with, we'll define a DataProtectionResponse (we're going to use the exact language from the domain model, the purpose of DDD is to translate the human-language into code).



          class DataProtectionResponse 
          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;



          Now, we need to come up with a model for DataProtectionQuestion:



          class DataProtectionQuestion 
          public string Question get; set;
          public bool Required get; set;



          As you see, we are ONLY modeling two components of the question: the actual question, and whether or not it's required. The questions themselves are a different part of the domain, they're generated as a question, and using this is how we get into building a flexible model. We can now take these same questions somewhere else, and use them as a whole other tool, assuming it needs to interact with our current domain.



          Next, we have ValidQuestionAnswer. This is going to be the answer that are valid for this particular claim:



          class ValidQuestionAnswer 
          public Response Response get; set;



          We made this a class as we absolutely want to consider a situation where an answer might have more data to it.



          Finally, the Response. You might say, "Der Kommissar, why does that need to be a class, it's always a string?" Again, we might need to add more to this model, including functionality, so we do that by using a class.



          class Response 
          public string Value get; set;



          So now, our domain will consume an IEnumerable<DataProtectionResponse>, but not directly.



          class DataProtection 
          public IEnumerable<DataProtectionResponse> Questions get; set;



          Why another class? Well, let's start talking functionality.



          First and foremost, the primary component of our design is that DataProtection must validate. For this to work, we need a IsValid function or property there:



          public bool IsValid => Questions.All(x => x.IsSufficient);


          Alright, so we have some concepts now. We have a IsValid that indicates if our DataProtection is valid or not, and we have decided that all of the questions must be sufficiently answered.



          Next, we need to prove that a question is sufficiently answered.



          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));


          Again, we are going to encode our actual logic: this DataProtectionResponse is sufficient if any of the ValidQuestionAnswers are acceptable with the question and response.



          Next, how do we prove they're acceptable?



          Well, the first rule is that if it's not required and there is no response, then it's valid:



          if (!question.Required && response?.IsEmpty ?? false == false)

          return true;



          And of course, Response.IsEmpty:



          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);


          Otherwise, we want to prove that this response and the provided response are acceptable:



          return response.Satisfies(Response);


          And this is why we made it a class right-off-the-bat: we might have more logic that goes into Satisfies that might do heuristic analysis. I.e. if you provide an address, the logic might say 123 Main St. and 123 Main St and 123 Main Street are all the same.



          public bool Acceptable(DataProtectionQuestion question, Response response)

          if (!question.Required && response?.IsEmpty ?? true)

          return true;


          return response.Satisfies(Response);



          Next, our Response.Satisfies:



          public bool Satisfies(Response response) => Value == response.Value;


          And viola, we're done. We've encoded the entire domain, concisely, and using the actual terms the domain considers. Only 37 lines of code:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool Acceptable(DataProtectionQuestion question, Response response)

          if (!question.Required && response?.IsEmpty ?? true)

          return true;


          return response.Satisfies(Response);


          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient);



          We don't have any odd logic, we don't have any conflated values: each model concerns itself with it's own work, no one else's.



          Additionally, when our domain changes now (or we have to change the satisfaction logic) we have built the flexibility in-place without needing major infrastructure rewrites. If we need to override a response, we encode that in DataProtectionResponse and modify IsSufficient.



          Finally, you could even shorten Acceptable to a single statement, since the logic is relatively straightforward:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true)
          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient);




          You noted in the comments that I missed the requirement of at least one optional question, and you're absolutely right. So let's talk about another strength of DDD: verifying our code matches the domain.




          At least one non-mandatory question must be answered in order to validate the caller.




          So, looking through our code we see that we can start with DataProtection.IsValid:



          public bool IsValid => Questions.All(x => x.IsSufficient);


          Aha, we already know our problem is here. We make sure all questions are sufficient, but that does not take at least one optional into account. So how do we fix that?



          Well, to start, we'll modify IsValid to support some theoretical work:



          public bool IsValid => Questions.All(x => x.IsSufficient) && Questions.Where(x => !x.IsRequired).Any(x => x.IsAnswered);


          Here, we've decided that if each DataProtectionResponse tells us if it's required or not, and if it's answered or not, we can prove that we have at least one non-required question answered.



          Next, we need to implement those two items. Both are trivial, and actually help with our other code:



          public bool IsRequired => Question.Required;
          public bool IsAnswered => ValidQuestionAnswers.Any(x => x.AnsweredBy(Response));


          Now we have another method to implement: ValidQuestionAnswer.AnsweredBy(Response):



          public bool AnsweredBy(Response response) => response.Satisfies(Response);


          You'll notice we've repeated one bit of code: response.Satisfies(Response) is in both AnsweredBy, and Acceptable, let's change that:



          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true) || AnsweredBy(Response);


          Once again, our contract is now satisfied, and we ONLY go at-most one-level-deep in each model from a parent model. (That is, we could have done !x.Question.Required instead of !x.IsRequired, but it's not the responsibility of DataProtection to know what makes a response required or not.)



          So, 36 lines of code to build our new requirements:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool AnsweredBy(Response response) => response.Satisfies(Response);

          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true)
          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;

          public bool IsRequired => Question.Required;
          public bool IsAnswered => ValidQuestionAnswers.Any(x => x.AnsweredBy(Response));
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient) && Questions.Where(x => !x.IsRequired).Any(x => x.IsAnswered);




          Finally, you asked a few questions about your implementation: as you see, I ignored them, purposefully. With DDD, those questions are not things to ask about the implementation but things to ask about the domain. With DDD we do iterations of "design", "implement", "design", "implement"—all the questions you have should go in the design stage, which is where you (and the other domain experts) gather and hash-out the principles of the project. This means, now that you have an implementation, we go back to design and clarify those questions. As the developer, when you see these things you should be creating a working list of potential problem-points, you might find out the domain-experts have considered them, or they may not, so you take your concerns back to them and refine the design. (Again, DDD is an iterative concept.)



          But, suppose I were to answer them:




          Should the entity have methods like 'SetAnswer' or 'SetAnswerIsValid'?




          A: That's actually a design question, do you need to override whether an answer is valid or not? (I briefly touched on that in the first part of the answer.)




          Should the setters be private and should clients supply data through a constructor?




          A: This seems like an implementation question at first glance, but if we reword it things are different: can a client change an answer? If the answer is yes, the proposed design is fine. If not, model for immutability.




          Should an Answer be an entity in its own right with a property for 'IsValid'?




          A: In my humble opinion, yes. Answers aren't a string, they're a concept. Additionally, with a base-class, you can override that for answers that are bool, DateTime, etc. But, again: take it back to the DDD drawing board. The domain model will tell you what needs done.




          How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'?




          A: That's a design question, but my suggestion is to ditch the Valid checkbox and provide a red/green "Needs Answered", "Incorrect Answer", or "Correct Answer" state. Again, do what the domain calls for. You're mixing some concerns here, and with DDD we create a clear separation. When you have a question like this, we go back into the design stage and hash it out. (It might turn out that you must not indicate if an answer is incorrect. Compliance laws are weird.)






          share|improve this answer











          $endgroup$












          • $begingroup$
            Thank you for taking the time to write such a comprehensive response. It is much appreciated. You're absolutely right when you say I'm mixing concerns. I am struggling to get my mindset away from data-driven towards domain-drive design. Your suggested approach to DDD has given me much needed food for thought. One thing I mentioned in my original post that I don't think your answer covers: "At least one non-mandatory question must be answered in order to validate the caller." I believe the code you have suggested allows for all non-mandatory questions to be left unanswered?
            $endgroup$
            – datahandler
            Apr 3 at 8:27










          • $begingroup$
            @datahandler I just edited the answer to address that (I did not change the existing answer, but instead showed how we would validate that the model we built conforms to the DDD specification). Hopefully all of this helps you figure out where you can modify the process to do DDD a little more effectively. :)
            $endgroup$
            – Der Kommissar
            Apr 3 at 13:24










          • $begingroup$
            @DerKommissar Excellent! Thanks again!
            $endgroup$
            – datahandler
            Apr 3 at 14:38













          17












          17








          17





          $begingroup$

          To start with, you're not doing DDD here.



          DDD (Domain-Driven Design / Development) is based around the idea that we start with the domain. We don't touch code yet—we develop the domain models on-paper (or whiteboard, whatever is preferred). Once that is done, we build the code as closely to the domain as possible. The point of DDD is that the code should mirror the domain design.



          Before we get going, I highly, highly, highly recommend this book, by Scott Wlaschin, a prominent F# developer who brings DDD into a very easy-to-understand view (the examples are F#, but they apply to C# as well): Domain Modeling made Functional



          DDD is about:




          1. Define the domain, the inputs, and the outputs. That is, as a user of the system, what does the domain need to do. Here it sounds like we have part of the domain defined:




            As part of an insurance claims system we have created, the claims managers can log incoming telephone calls relating to a claim.



            The claims manager must validate the caller by asking a number of 'Data Protection' questions that are generated dynamically from information stored against the claim in a database. I believe this type of security is known as 'knowledge-based authentication'.



            Notes about Data Protection Questions:



            • Some questions are mandatory and some are not.

            • All mandatory questions must be answered in order to validate the caller.

            • At least one non-mandatory question must be answered in order to validate the
              caller.

            • Additional non-mandatory questions can remain unanswered.

            • Each question may have multiple correct answers.




          2. From there, we define our types. Generally, I do DDD with F#, but it's just as applicable to C#. We model the physical domain, so here we're not modeling the questions, we're modeling the validation. That is: the user must answer various questions and prove they are knowledgeable on the claim.



            This is the root of our domain model: we need to validate some information. You have mixed multiple pieces here, so we're going to separate them a bit.



          3. After building the types, we build the work. That is, the functions. We build the types as just data-structures, then we build the functions next to encapsulate the domain rules.


          So, you've defined the domain (at least, as far as I see it) via the quoted-blurb, so what I want to do is move that into some types.



          To start with, we'll define a DataProtectionResponse (we're going to use the exact language from the domain model, the purpose of DDD is to translate the human-language into code).



          class DataProtectionResponse 
          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;



          Now, we need to come up with a model for DataProtectionQuestion:



          class DataProtectionQuestion 
          public string Question get; set;
          public bool Required get; set;



          As you see, we are ONLY modeling two components of the question: the actual question, and whether or not it's required. The questions themselves are a different part of the domain, they're generated as a question, and using this is how we get into building a flexible model. We can now take these same questions somewhere else, and use them as a whole other tool, assuming it needs to interact with our current domain.



          Next, we have ValidQuestionAnswer. This is going to be the answer that are valid for this particular claim:



          class ValidQuestionAnswer 
          public Response Response get; set;



          We made this a class as we absolutely want to consider a situation where an answer might have more data to it.



          Finally, the Response. You might say, "Der Kommissar, why does that need to be a class, it's always a string?" Again, we might need to add more to this model, including functionality, so we do that by using a class.



          class Response 
          public string Value get; set;



          So now, our domain will consume an IEnumerable<DataProtectionResponse>, but not directly.



          class DataProtection 
          public IEnumerable<DataProtectionResponse> Questions get; set;



          Why another class? Well, let's start talking functionality.



          First and foremost, the primary component of our design is that DataProtection must validate. For this to work, we need a IsValid function or property there:



          public bool IsValid => Questions.All(x => x.IsSufficient);


          Alright, so we have some concepts now. We have a IsValid that indicates if our DataProtection is valid or not, and we have decided that all of the questions must be sufficiently answered.



          Next, we need to prove that a question is sufficiently answered.



          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));


          Again, we are going to encode our actual logic: this DataProtectionResponse is sufficient if any of the ValidQuestionAnswers are acceptable with the question and response.



          Next, how do we prove they're acceptable?



          Well, the first rule is that if it's not required and there is no response, then it's valid:



          if (!question.Required && response?.IsEmpty ?? false == false)

          return true;



          And of course, Response.IsEmpty:



          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);


          Otherwise, we want to prove that this response and the provided response are acceptable:



          return response.Satisfies(Response);


          And this is why we made it a class right-off-the-bat: we might have more logic that goes into Satisfies that might do heuristic analysis. I.e. if you provide an address, the logic might say 123 Main St. and 123 Main St and 123 Main Street are all the same.



          public bool Acceptable(DataProtectionQuestion question, Response response)

          if (!question.Required && response?.IsEmpty ?? true)

          return true;


          return response.Satisfies(Response);



          Next, our Response.Satisfies:



          public bool Satisfies(Response response) => Value == response.Value;


          And viola, we're done. We've encoded the entire domain, concisely, and using the actual terms the domain considers. Only 37 lines of code:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool Acceptable(DataProtectionQuestion question, Response response)

          if (!question.Required && response?.IsEmpty ?? true)

          return true;


          return response.Satisfies(Response);


          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient);



          We don't have any odd logic, we don't have any conflated values: each model concerns itself with it's own work, no one else's.



          Additionally, when our domain changes now (or we have to change the satisfaction logic) we have built the flexibility in-place without needing major infrastructure rewrites. If we need to override a response, we encode that in DataProtectionResponse and modify IsSufficient.



          Finally, you could even shorten Acceptable to a single statement, since the logic is relatively straightforward:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true)
          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient);




          You noted in the comments that I missed the requirement of at least one optional question, and you're absolutely right. So let's talk about another strength of DDD: verifying our code matches the domain.




          At least one non-mandatory question must be answered in order to validate the caller.




          So, looking through our code we see that we can start with DataProtection.IsValid:



          public bool IsValid => Questions.All(x => x.IsSufficient);


          Aha, we already know our problem is here. We make sure all questions are sufficient, but that does not take at least one optional into account. So how do we fix that?



          Well, to start, we'll modify IsValid to support some theoretical work:



          public bool IsValid => Questions.All(x => x.IsSufficient) && Questions.Where(x => !x.IsRequired).Any(x => x.IsAnswered);


          Here, we've decided that if each DataProtectionResponse tells us if it's required or not, and if it's answered or not, we can prove that we have at least one non-required question answered.



          Next, we need to implement those two items. Both are trivial, and actually help with our other code:



          public bool IsRequired => Question.Required;
          public bool IsAnswered => ValidQuestionAnswers.Any(x => x.AnsweredBy(Response));


          Now we have another method to implement: ValidQuestionAnswer.AnsweredBy(Response):



          public bool AnsweredBy(Response response) => response.Satisfies(Response);


          You'll notice we've repeated one bit of code: response.Satisfies(Response) is in both AnsweredBy, and Acceptable, let's change that:



          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true) || AnsweredBy(Response);


          Once again, our contract is now satisfied, and we ONLY go at-most one-level-deep in each model from a parent model. (That is, we could have done !x.Question.Required instead of !x.IsRequired, but it's not the responsibility of DataProtection to know what makes a response required or not.)



          So, 36 lines of code to build our new requirements:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool AnsweredBy(Response response) => response.Satisfies(Response);

          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true)
          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;

          public bool IsRequired => Question.Required;
          public bool IsAnswered => ValidQuestionAnswers.Any(x => x.AnsweredBy(Response));
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient) && Questions.Where(x => !x.IsRequired).Any(x => x.IsAnswered);




          Finally, you asked a few questions about your implementation: as you see, I ignored them, purposefully. With DDD, those questions are not things to ask about the implementation but things to ask about the domain. With DDD we do iterations of "design", "implement", "design", "implement"—all the questions you have should go in the design stage, which is where you (and the other domain experts) gather and hash-out the principles of the project. This means, now that you have an implementation, we go back to design and clarify those questions. As the developer, when you see these things you should be creating a working list of potential problem-points, you might find out the domain-experts have considered them, or they may not, so you take your concerns back to them and refine the design. (Again, DDD is an iterative concept.)



          But, suppose I were to answer them:




          Should the entity have methods like 'SetAnswer' or 'SetAnswerIsValid'?




          A: That's actually a design question, do you need to override whether an answer is valid or not? (I briefly touched on that in the first part of the answer.)




          Should the setters be private and should clients supply data through a constructor?




          A: This seems like an implementation question at first glance, but if we reword it things are different: can a client change an answer? If the answer is yes, the proposed design is fine. If not, model for immutability.




          Should an Answer be an entity in its own right with a property for 'IsValid'?




          A: In my humble opinion, yes. Answers aren't a string, they're a concept. Additionally, with a base-class, you can override that for answers that are bool, DateTime, etc. But, again: take it back to the DDD drawing board. The domain model will tell you what needs done.




          How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'?




          A: That's a design question, but my suggestion is to ditch the Valid checkbox and provide a red/green "Needs Answered", "Incorrect Answer", or "Correct Answer" state. Again, do what the domain calls for. You're mixing some concerns here, and with DDD we create a clear separation. When you have a question like this, we go back into the design stage and hash it out. (It might turn out that you must not indicate if an answer is incorrect. Compliance laws are weird.)






          share|improve this answer











          $endgroup$



          To start with, you're not doing DDD here.



          DDD (Domain-Driven Design / Development) is based around the idea that we start with the domain. We don't touch code yet—we develop the domain models on-paper (or whiteboard, whatever is preferred). Once that is done, we build the code as closely to the domain as possible. The point of DDD is that the code should mirror the domain design.



          Before we get going, I highly, highly, highly recommend this book, by Scott Wlaschin, a prominent F# developer who brings DDD into a very easy-to-understand view (the examples are F#, but they apply to C# as well): Domain Modeling made Functional



          DDD is about:




          1. Define the domain, the inputs, and the outputs. That is, as a user of the system, what does the domain need to do. Here it sounds like we have part of the domain defined:




            As part of an insurance claims system we have created, the claims managers can log incoming telephone calls relating to a claim.



            The claims manager must validate the caller by asking a number of 'Data Protection' questions that are generated dynamically from information stored against the claim in a database. I believe this type of security is known as 'knowledge-based authentication'.



            Notes about Data Protection Questions:



            • Some questions are mandatory and some are not.

            • All mandatory questions must be answered in order to validate the caller.

            • At least one non-mandatory question must be answered in order to validate the
              caller.

            • Additional non-mandatory questions can remain unanswered.

            • Each question may have multiple correct answers.




          2. From there, we define our types. Generally, I do DDD with F#, but it's just as applicable to C#. We model the physical domain, so here we're not modeling the questions, we're modeling the validation. That is: the user must answer various questions and prove they are knowledgeable on the claim.



            This is the root of our domain model: we need to validate some information. You have mixed multiple pieces here, so we're going to separate them a bit.



          3. After building the types, we build the work. That is, the functions. We build the types as just data-structures, then we build the functions next to encapsulate the domain rules.


          So, you've defined the domain (at least, as far as I see it) via the quoted-blurb, so what I want to do is move that into some types.



          To start with, we'll define a DataProtectionResponse (we're going to use the exact language from the domain model, the purpose of DDD is to translate the human-language into code).



          class DataProtectionResponse 
          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;



          Now, we need to come up with a model for DataProtectionQuestion:



          class DataProtectionQuestion 
          public string Question get; set;
          public bool Required get; set;



          As you see, we are ONLY modeling two components of the question: the actual question, and whether or not it's required. The questions themselves are a different part of the domain, they're generated as a question, and using this is how we get into building a flexible model. We can now take these same questions somewhere else, and use them as a whole other tool, assuming it needs to interact with our current domain.



          Next, we have ValidQuestionAnswer. This is going to be the answer that are valid for this particular claim:



          class ValidQuestionAnswer 
          public Response Response get; set;



          We made this a class as we absolutely want to consider a situation where an answer might have more data to it.



          Finally, the Response. You might say, "Der Kommissar, why does that need to be a class, it's always a string?" Again, we might need to add more to this model, including functionality, so we do that by using a class.



          class Response 
          public string Value get; set;



          So now, our domain will consume an IEnumerable<DataProtectionResponse>, but not directly.



          class DataProtection 
          public IEnumerable<DataProtectionResponse> Questions get; set;



          Why another class? Well, let's start talking functionality.



          First and foremost, the primary component of our design is that DataProtection must validate. For this to work, we need a IsValid function or property there:



          public bool IsValid => Questions.All(x => x.IsSufficient);


          Alright, so we have some concepts now. We have a IsValid that indicates if our DataProtection is valid or not, and we have decided that all of the questions must be sufficiently answered.



          Next, we need to prove that a question is sufficiently answered.



          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));


          Again, we are going to encode our actual logic: this DataProtectionResponse is sufficient if any of the ValidQuestionAnswers are acceptable with the question and response.



          Next, how do we prove they're acceptable?



          Well, the first rule is that if it's not required and there is no response, then it's valid:



          if (!question.Required && response?.IsEmpty ?? false == false)

          return true;



          And of course, Response.IsEmpty:



          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);


          Otherwise, we want to prove that this response and the provided response are acceptable:



          return response.Satisfies(Response);


          And this is why we made it a class right-off-the-bat: we might have more logic that goes into Satisfies that might do heuristic analysis. I.e. if you provide an address, the logic might say 123 Main St. and 123 Main St and 123 Main Street are all the same.



          public bool Acceptable(DataProtectionQuestion question, Response response)

          if (!question.Required && response?.IsEmpty ?? true)

          return true;


          return response.Satisfies(Response);



          Next, our Response.Satisfies:



          public bool Satisfies(Response response) => Value == response.Value;


          And viola, we're done. We've encoded the entire domain, concisely, and using the actual terms the domain considers. Only 37 lines of code:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool Acceptable(DataProtectionQuestion question, Response response)

          if (!question.Required && response?.IsEmpty ?? true)

          return true;


          return response.Satisfies(Response);


          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient);



          We don't have any odd logic, we don't have any conflated values: each model concerns itself with it's own work, no one else's.



          Additionally, when our domain changes now (or we have to change the satisfaction logic) we have built the flexibility in-place without needing major infrastructure rewrites. If we need to override a response, we encode that in DataProtectionResponse and modify IsSufficient.



          Finally, you could even shorten Acceptable to a single statement, since the logic is relatively straightforward:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true)
          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient);




          You noted in the comments that I missed the requirement of at least one optional question, and you're absolutely right. So let's talk about another strength of DDD: verifying our code matches the domain.




          At least one non-mandatory question must be answered in order to validate the caller.




          So, looking through our code we see that we can start with DataProtection.IsValid:



          public bool IsValid => Questions.All(x => x.IsSufficient);


          Aha, we already know our problem is here. We make sure all questions are sufficient, but that does not take at least one optional into account. So how do we fix that?



          Well, to start, we'll modify IsValid to support some theoretical work:



          public bool IsValid => Questions.All(x => x.IsSufficient) && Questions.Where(x => !x.IsRequired).Any(x => x.IsAnswered);


          Here, we've decided that if each DataProtectionResponse tells us if it's required or not, and if it's answered or not, we can prove that we have at least one non-required question answered.



          Next, we need to implement those two items. Both are trivial, and actually help with our other code:



          public bool IsRequired => Question.Required;
          public bool IsAnswered => ValidQuestionAnswers.Any(x => x.AnsweredBy(Response));


          Now we have another method to implement: ValidQuestionAnswer.AnsweredBy(Response):



          public bool AnsweredBy(Response response) => response.Satisfies(Response);


          You'll notice we've repeated one bit of code: response.Satisfies(Response) is in both AnsweredBy, and Acceptable, let's change that:



          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true) || AnsweredBy(Response);


          Once again, our contract is now satisfied, and we ONLY go at-most one-level-deep in each model from a parent model. (That is, we could have done !x.Question.Required instead of !x.IsRequired, but it's not the responsibility of DataProtection to know what makes a response required or not.)



          So, 36 lines of code to build our new requirements:



          class Response

          public string Value get; set;
          public bool IsEmpty => String.IsNullOrWhiteSpace(Value);

          public bool Satisfies(Response response) => Value == response.Value;

          class ValidQuestionAnswer

          public Response Response get; set;

          public bool AnsweredBy(Response response) => response.Satisfies(Response);

          public bool Acceptable(DataProtectionQuestion question, Response response) =>
          (!question.Required && response?.IsEmpty ?? true)
          class DataProtectionQuestion

          public string Question get; set;
          public bool Required get; set;

          class DataProtectionResponse

          public DataProtectionQuestion Question get; set;
          public IEnumerable<ValidQuestionAnswer> ValidQuestionAnswers get; set;
          public Response Response get; set;

          public bool IsRequired => Question.Required;
          public bool IsAnswered => ValidQuestionAnswers.Any(x => x.AnsweredBy(Response));
          public bool IsSufficient => ValidQuestionAnswers.Any(x => x.Acceptable(Question, Response));

          class DataProtection

          public IEnumerable<DataProtectionResponse> Questions get; set;
          public bool IsValid => Questions.All(x => x.IsSufficient) && Questions.Where(x => !x.IsRequired).Any(x => x.IsAnswered);




          Finally, you asked a few questions about your implementation: as you see, I ignored them, purposefully. With DDD, those questions are not things to ask about the implementation but things to ask about the domain. With DDD we do iterations of "design", "implement", "design", "implement"—all the questions you have should go in the design stage, which is where you (and the other domain experts) gather and hash-out the principles of the project. This means, now that you have an implementation, we go back to design and clarify those questions. As the developer, when you see these things you should be creating a working list of potential problem-points, you might find out the domain-experts have considered them, or they may not, so you take your concerns back to them and refine the design. (Again, DDD is an iterative concept.)



          But, suppose I were to answer them:




          Should the entity have methods like 'SetAnswer' or 'SetAnswerIsValid'?




          A: That's actually a design question, do you need to override whether an answer is valid or not? (I briefly touched on that in the first part of the answer.)




          Should the setters be private and should clients supply data through a constructor?




          A: This seems like an implementation question at first glance, but if we reword it things are different: can a client change an answer? If the answer is yes, the proposed design is fine. If not, model for immutability.




          Should an Answer be an entity in its own right with a property for 'IsValid'?




          A: In my humble opinion, yes. Answers aren't a string, they're a concept. Additionally, with a base-class, you can override that for answers that are bool, DateTime, etc. But, again: take it back to the DDD drawing board. The domain model will tell you what needs done.




          How do I display answers in the UI to include 'Unanswered' and 'Incorrect Answer'?




          A: That's a design question, but my suggestion is to ditch the Valid checkbox and provide a red/green "Needs Answered", "Incorrect Answer", or "Correct Answer" state. Again, do what the domain calls for. You're mixing some concerns here, and with DDD we create a clear separation. When you have a question like this, we go back into the design stage and hash it out. (It might turn out that you must not indicate if an answer is incorrect. Compliance laws are weird.)







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited yesterday

























          answered Apr 2 at 16:19









          Der KommissarDer Kommissar

          15.8k251136




          15.8k251136











          • $begingroup$
            Thank you for taking the time to write such a comprehensive response. It is much appreciated. You're absolutely right when you say I'm mixing concerns. I am struggling to get my mindset away from data-driven towards domain-drive design. Your suggested approach to DDD has given me much needed food for thought. One thing I mentioned in my original post that I don't think your answer covers: "At least one non-mandatory question must be answered in order to validate the caller." I believe the code you have suggested allows for all non-mandatory questions to be left unanswered?
            $endgroup$
            – datahandler
            Apr 3 at 8:27










          • $begingroup$
            @datahandler I just edited the answer to address that (I did not change the existing answer, but instead showed how we would validate that the model we built conforms to the DDD specification). Hopefully all of this helps you figure out where you can modify the process to do DDD a little more effectively. :)
            $endgroup$
            – Der Kommissar
            Apr 3 at 13:24










          • $begingroup$
            @DerKommissar Excellent! Thanks again!
            $endgroup$
            – datahandler
            Apr 3 at 14:38
















          • $begingroup$
            Thank you for taking the time to write such a comprehensive response. It is much appreciated. You're absolutely right when you say I'm mixing concerns. I am struggling to get my mindset away from data-driven towards domain-drive design. Your suggested approach to DDD has given me much needed food for thought. One thing I mentioned in my original post that I don't think your answer covers: "At least one non-mandatory question must be answered in order to validate the caller." I believe the code you have suggested allows for all non-mandatory questions to be left unanswered?
            $endgroup$
            – datahandler
            Apr 3 at 8:27










          • $begingroup$
            @datahandler I just edited the answer to address that (I did not change the existing answer, but instead showed how we would validate that the model we built conforms to the DDD specification). Hopefully all of this helps you figure out where you can modify the process to do DDD a little more effectively. :)
            $endgroup$
            – Der Kommissar
            Apr 3 at 13:24










          • $begingroup$
            @DerKommissar Excellent! Thanks again!
            $endgroup$
            – datahandler
            Apr 3 at 14:38















          $begingroup$
          Thank you for taking the time to write such a comprehensive response. It is much appreciated. You're absolutely right when you say I'm mixing concerns. I am struggling to get my mindset away from data-driven towards domain-drive design. Your suggested approach to DDD has given me much needed food for thought. One thing I mentioned in my original post that I don't think your answer covers: "At least one non-mandatory question must be answered in order to validate the caller." I believe the code you have suggested allows for all non-mandatory questions to be left unanswered?
          $endgroup$
          – datahandler
          Apr 3 at 8:27




          $begingroup$
          Thank you for taking the time to write such a comprehensive response. It is much appreciated. You're absolutely right when you say I'm mixing concerns. I am struggling to get my mindset away from data-driven towards domain-drive design. Your suggested approach to DDD has given me much needed food for thought. One thing I mentioned in my original post that I don't think your answer covers: "At least one non-mandatory question must be answered in order to validate the caller." I believe the code you have suggested allows for all non-mandatory questions to be left unanswered?
          $endgroup$
          – datahandler
          Apr 3 at 8:27












          $begingroup$
          @datahandler I just edited the answer to address that (I did not change the existing answer, but instead showed how we would validate that the model we built conforms to the DDD specification). Hopefully all of this helps you figure out where you can modify the process to do DDD a little more effectively. :)
          $endgroup$
          – Der Kommissar
          Apr 3 at 13:24




          $begingroup$
          @datahandler I just edited the answer to address that (I did not change the existing answer, but instead showed how we would validate that the model we built conforms to the DDD specification). Hopefully all of this helps you figure out where you can modify the process to do DDD a little more effectively. :)
          $endgroup$
          – Der Kommissar
          Apr 3 at 13:24












          $begingroup$
          @DerKommissar Excellent! Thanks again!
          $endgroup$
          – datahandler
          Apr 3 at 14:38




          $begingroup$
          @DerKommissar Excellent! Thanks again!
          $endgroup$
          – datahandler
          Apr 3 at 14:38










          datahandler is a new contributor. Be nice, and check out our Code of Conduct.









          draft saved

          draft discarded


















          datahandler is a new contributor. Be nice, and check out our Code of Conduct.












          datahandler is a new contributor. Be nice, and check out our Code of Conduct.











          datahandler is a new contributor. Be nice, and check out our Code of Conduct.














          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid


          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.

          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216727%2fknowledge-based-authentication-using-domain-driven-design-in-c%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Distance measures on a map of a game The 2019 Stack Overflow Developer Survey Results Are Inmin distance in a graphShortest distance path on contour plotHow to plot a tilted map?Finding points outside of a diskDelaunay link distanceAnnulus from GeoDisks: drawing a ring on a mapNegative Correlation DistanceFind distance along a path (GPS coordinates)Finding position at given distance in a GeoPathMathematics behind distance estimation using camera

          How to get a smooth, uniform ParametricPlot of a 2D Region?How to plot a complicated Region?How to exclude a region from ParametricPlotHow discretize a region placing vertices on a specific non-uniform gridHow to transform a Plot or a ParametricPlot into a RegionHow can I get a smooth plot of a bounded region?Smooth ParametricPlot3D with RegionFunction?Smooth border of a region ParametricPlotSmooth region boundarySmooth region plot from list of pointsGet minimum y of a certain x in a region

          Genealogie vun de Merowenger Vum Merowech bis zum Chilperich I. | Navigatiounsmenü