Reputation: 7369
I am currently thinking about with what pattern I should solve the following issue.
I've got an entity called IdentificationRequest
. This entity is used to identify a Person
by some criteria.
public class IdentificationRequest
{
public IdentificationCriteria Criteria;
public Person IdentifiedPerson;
protected internal virtual void RedirectToManualIdentification()
{
ChangeState(IdentificationRequestState.ManualIdentificationRequested);
}
public virtual void StartManualIdentification()
{
ChangeState(IdentificationRequestState.ManualIdentificationInProgress);
}
public virtual void AssignIdentifiedPerson(Person person)
{
identifiedPerson = person;
ChangeState(IdentificationRequestState.IdentificationFinished);
}
}
public class IdentificationCriteria
{
public string Name;
}
This is a simplified example. In realitiy the IdentificationRequest
contains much more information, as well as the IdentificationCriteria
.
So basically a client creates an IdentificationRequest
with its IdentificationCriteria
and then the correct Person
needs to be identified. For this, the criteria needs to be passed to the persistence layer to check whether there is a person in the database matching the criteria. If not person can be found, a human interaction is then required to assign the correct Person
to the request.
For the process of identification I am currently using a service. Like:
public class IdentificationService : IIdentificationService
{
private readonly IPersonRepository personRepository ;
private readonly IIdentificationRequestRepository identificationRequestRepository;
public IdentificationService(IPersonRepository personRepository )
{
this.personRepository = personRepository ;
}
public bool IdentifyPerson(IdentificationRequest identificationRequest)
{
var matches = personRepository.FindByIdentificationCriteria(identificationRequest.Criteria);
// some additional post analysis of the matches returned from the persistence layer
var criteriaAnalyzer = new IdentificationCriteriaAnalyzer(identificationRequest.Criteria);
var uniqueMatch = criteriaAnalyzer.TryIdentify(matches);
if(uniqueMatch != null)
{
identificationRequest.AssignIdentifiedPerson(uniqueMatch);
return true;
}
else
{
identificationRequest.RedirectToManualIdentification();
return false;
}
}
}
This service is part of the domain assembly. Now my question is, if that is the right pattern to perform the identification? Or would I use a factory, to create the identification request and then directly try to identify it, like:
public class IdentificationRequestFactory
{
private readonly IPersonRepository personRepository;
public IdentificationRequestFactory(IPersonRepository personRepository)
{
this.personRepository = personRepository;
}
public IdentificationRequest Create(IdentificationCriteria identificationCriteria)
{
var request = new IdentificationRequest(identificationCriteria);
var matches = personRepository.FindByIdentificationCriteria(identificationRequest.Criteria);
var criteriaAnalyzer = new IdentificationCriteriaAnalyzer(identificationRequest.Criteria);
var uniqueMatch = criteriaAnalyzer.TryIdentify(matches);
if(uniqueMatch != null)
{
identificationRequest.AssignIdentifiedPerson(uniqueMatch);
}
else
{
identificationRequest.RedirectToManualIdentification();
}
return request;
}
}
This way, an IdentificationRequest
can only be constructed by the factory, making sure that the process of identification is done already and the request is in a valid state.
Or would you let the IdentificationRequest
identify itself by doing a method injection like:
public class IdentificationRequest
{
public IdentificationCriteria Criteria;
public Person IdentifiedPerson;
public void Identify(IPersonRepository personRepository)
{
// identification logic here
}
}
This example would couple the process of identification to the request directly.
What is a common pattern for such a case? Is there a common pattern anyway? What are the pros and cons?
Thx in advance!
Update
Maybe I dont understand the command pattern correctly, but what benefits do I get of it in this case? Is the following implementation correct?
public class IdentificationCommandFactory
{
private readonly IPersonRepository personRepository;
public IdentificationCommandFactory(IPersonRepository personRepository)
{
this.personRepository = personRepository;
}
public IIdentificationCommand Create(IdentificationRequest identificationRequest)
{
var matches = personRepository.FindByIdentificationCriteria(identificationRequest);
var criteriaAnalyzer = new IdentificationCriteriaAnalyzer(identificationRequest);
var uniqueMatch = criteriaAnalyzer.TryIdentify(matches);
if(uniqueMatch != null)
{
return new AssignIdentifiedPersonCommand(identificationRequest, uniqueMatch);
}
else
{
return new RedirectToManualIdentificationCommand(identificationRequest);
}
}
}
public interface IIdentificationCommand
{
void Execute();
}
public class RedirectToManualIdentificationCommand : IIdentificationCommand
{
private readonly IdentificationRequest identificationRequest;
public RedirectToManualIdentificationCommand(IdentificationRequest identificationRequest)
{
this.identificationRequest = identificationRequest;
}
public void Execute()
{
identificationRequest.RedirectToManualIdentification();
}
}
public class AssignIdentifiedPersonCommand : IIdentificationCommand
{
private readonly IdentificationRequest identificationRequest;
private readonly Person personIdentified;
public AssignIdentifiedPersonCommand(IdentificationRequest identificationRequest, Person personIdentified)
{
this.identificationRequest = identificationRequest;
this.personIdentified = personIdentified;
}
public void Execute()
{
identificationRequest.AssignIdentifiedPerson(personIdentified);
}
}
The caller:
var identificationCommandFactory = new IdentificationCommandFactory(personRepository);
var command = identificationCommandFactory.Create(request);
command.Execute();
Upvotes: 3
Views: 2565
Reputation: 22717
The primary goal of your development should be to meet the requirements with the simplest, cleanest code possible. (http://msdn.microsoft.com/en-us/magazine/cc163962.aspx)
With that said, unless you have a specific reason to use one of these patterns, stop now. If there is no specific reason to go to this work, you're just writing more code in the hope that it will be useful someday. Just write what works. Refactor it later, if the need arises.
If the need is present, continue down the path of keeping it simple. Pick a pattern, library, or other tool that will allow you to write clean, simple, readable code that does the job.
Upvotes: 5
Reputation: 8534
It looks to me like you should be using the Command Pattern:
- http://www.dofactory.com/Patterns/PatternCommand.aspx
- http://c2.com/cgi/wiki?CommandPattern
- http://en.wikipedia.org/wiki/Command_pattern
Also your IdentificationRequest doesn't feel like an entity class, but I would describe your Person class as an entity.
Often the Invoker within the Command Pattern send a response to the receiver, indicating the success or failure, and in the case of success, then the response object could contain the identifiedPerson.
Upvotes: 0
Reputation: 233197
Both your IdentificationCriteria and IdentificationCriteriaAnalyzer types look to me a lot like Specifications, so I would see if it makes sense to refactor them in that direction.
The IdentificationRequest class seems to me to be breaking the Single Responsibility Principle - why does it have a Person field/property?
The API could also benefit from better Command-Query Separation.
So I think a better model would be to define an interface that takes an IdentificationCriteria and returns some kind of Command. Such an interface would essentiall be an Abstract Factory.
public interface IIdentificationCommandFactory
{
IIdentificationCommand Create(IdentificationCriteria spec);
}
When you implement it, you can look for matches in your repostiry, and based on the result of that query and your other Specifications (such as the IdentificationCriteriaAnalyzer) you can return the correct implementation of IIdentificationCommand.
The caller would then ask the returned command to execute.
Upvotes: 0