I have a model Administrator that has its properties, but it also consists of numerous static methods that do not really tied any way to the current object itself like for example GetByCredentials(string username, string password);. Is it somehow possible to divide static methods someplace else and lave object as pure as possible?
Example
public class Administrator : Entity
{
// OBJECT START
public int Id { get; set; }
public DateTime CreatedDateTime { get; set; }
public DateTime UpdatedDateTime { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string PasswordSalt { get; set; }
public void SetNewPassword(string password)
{
var cryptoService = new PBKDF2();
this.Password = cryptoService.Compute(password);
this.PasswordSalt = cryptoService.Salt;
}
public override void OnBeforeInsert()
{
this.CreatedDateTime = DateTime.Now;
this.UpdatedDateTime = DateTime.Now;
this.SetNewPassword(this.Password);
}
public override void OnBeforeUpdate()
{
this.UpdatedDateTime = DateTime.Now;
}
// OBJECT END
// Now I have multiple static methods that do not really
// have anything to do with current object
public static Administrator GetByCredentials(string username, string password)
{
var db = new MainDataContext();
var admin = db.Administrators.SingleOrDefault(x => x.Username == username);
if (admin == null) return null;
ICryptoService cryptoService = new PBKDF2();
var hash = cryptoService.Compute(password, admin.PasswordSalt);
if (hash == admin.Password) return admin;
return null;
}
public static bool IsCurrentIpBanned
{
get
{
const int minutesBlocked = 5;
const int maxLoginCount = 5;
var db = new MainDataContext();
var loginCount = db.AdministratorAuthorizationLogs.AsEnumerable().Count(x => x.Ip == HttpContext.Current.Request.UserHostAddress && x.CreatedDateTime.AddMinutes(minutesBlocked) > DateTime.Now && x.IsSuccess == false);
return loginCount > maxLoginCount;
}
}
public static void LogSuccess(Administrator admin)
{
Administrator.Log(admin, true);
}
public static void LogFailure(Administrator admin)
{
Administrator.Log(admin, false);
}
private static void Log(Administrator admin, bool success)
{
var db = new MainDataContext();
db.AdministratorAuthorizationLogs.Add(new AdministratorAuthorizationLog
{
Username = admin.Username,
Password = admin.Password,
Ip = HttpContext.Current.Request.UserHostAddress,
IsSuccess = success
});
db.SaveChanges();
}
}
There are several options here, but the main thing is that C# classes are the tool for separating concerns.
The most obvious is to capture those things in their own abstraction(s). For example, the GetByCredentials might be better as a (non-static) member of a different class Authority or similar. That class only needs to be able to create an Administrator type.
You can also use extension methods. A possible candidate for that is Log, which takes an Administrator as an argument and uses only public facilities on it. Extension methods are defined in a separate class, but allow you to use them "as if" they were members of the extended class, e.g.:
public static class AdministratorExtensions
{
public static void log( this Administrator admin, bool success ) { ... }
}
var admin = new Administrator();
admin.Log( true );
The key thing is to identify real abstractions and build your system up from them by combining them in sensible ways. Separating out concerns is part of that process.
This is a hint that your class "knows too much". The Administrator class should only know what concerns an administrator. He shouldn't be able to query the database and retrieve entities.
You should look into the repository pattern. Try to decompose your application into multiple layers. For example, you could have a DataRepository class whose main concern is to query and update the database entities.
Related
I'm building a small policy system for passwords and usernames. These policies can be configured based on a variety of different factors, but for the most part they're relatively straight forward. The policies all implement IPolicy which looks something like:
public interface IPolicy
{
(bool, ErrorResponse) Verify(string input);
}
Some of the policies require certain parameters to be passed to them during instantiation, such as minimumLength. An example policy may look something like:
public class LowerCasePolicy : IPolicy
{
private const string _defaultTitle = "LowerCaseCount";
private readonly int _minimumLength;
private readonly string _errorMessage;
private readonly string _errorTitle;
public LowerCasePolicy(int minimumLength)
{
_minimumLength = minimumLength;
_errorMessage =
$"Password does not meet the lower case character count requirement set by the password policy ({_minimumLength})";
_errorTitle = _defaultTitle;
}
public LowerCasePolicy(int minimumLength, string errorMessage, string errorTitle = _defaultTitle)
{
_minimumLength = minimumLength;
_errorMessage = errorMessage;
_errorTitle = errorTitle;
}
public (bool, ErrorResponse) Verify(string input)
{
var enoughUpper = Regex.Matches(input, "[a-z]").Count >= _minimumLength;
return !enoughUpper ?
(false, new ErrorResponse(_errorTitle, _errorMessage))
: (true, null);
}
}
I'm trying to build some sort of factory that's capable of returning all of my different policies with their different constructors, but I'm not too sure where to go from here. One potential option I came up with was to create a base PolicyArgs class to pass the parameters, and I could use derived classes for each. Like so:
public class PolicyArgs
{
public string Title { get; set; }
public string ErrorMessage { get; set; }
}
public class LowerCaseArgs : PolicyArgs
{
public int MinimumLength { get; set; }
}
And the constructor for the policy would now look like:
public LowerCasePolicy(PolicyArgs args)
{
if (args == null)
throw new ArgumentException();
if (!(args is LowerCaseArgs lowerCaseArgs))
throw new ArgumentException();
_minimumLength = lowerCaseArgs.MinimumLength;
_errorTitle = lowerCaseArgs.Title ?? _defaultTitle;
_errorMessage = lowerCaseArgs.ErrorMessage ?? $"Password does not meet the lower case character count requirement set by the password policy ({_minimumLength})";
}
And the factory would look like:
public class PolicyFactory
{
private readonly Dictionary<Policy, Func<PolicyArgs, IPolicy>> _policyDictionary = new Dictionary<Policy, Func<PolicyArgs, IPolicy>>
{
[Policy.LowerCase] = (args) => new LowerCasePolicy(args)
};
public IPolicy Create(Policy policy, PolicyArgs args)
{
return _policyDictionary[policy](args);
}
}
I'm not sure if this is truly the best approach, or if there's some better option for handling different policies with different constructor needs. The goal is to be able to simply put these configurations in a database and have my PolicyProvider effectively return an array of IPolicy.
As suggested, another option is to use something akin to a dictionary to handle the additional parameters, such as:
public class PolicyArgs
{
public string Title { get; set; }
public string ErrorMessage { get; set; }
// There's almost definitely a better way to handle this;
public Dictionary<string, object> AdditionalParameters { get; set; } = new Dictionary<string, object>();
}
public LowerCasePolicy(PolicyArgs args)
{
if (args == null)
throw new ArgumentException();
if (!args.AdditionalParameters.ContainsKey("MinimumLength"))
throw new ArgumentException("Minimum Length is not provided.");
if (!int.TryParse(args.AdditionalParameters["MinimumLength"].ToString(), out var minimumLength))
throw new ArgumentException("Minimum Length is invalid.");
_minimumLength = minimumLength;
_errorTitle = args.Title ?? _defaultTitle;
_errorMessage = args.ErrorMessage ?? $"Password does not meet the lower case character count requirement set by the password policy ({_minimumLength})";
}
This approach is great because it makes it incredibly easy to store the data as JSON either in the database or in the application configuration files. However, it has some distinct draw backs. For one, a type in any of the strings could be disastrous. It also requires me to manually handle the typecasting and checks.
I'm not opposed to this approach, but I feel like there are definitely better options out there.
The original usage (with strongly typed classes for each policy) looks like this:
public void TestPolicyFactory()
{
var args = new LowerCasePolicy.LowerCaseArgs {MinimumLength = 1};
var factory = new PolicyFactory();
var policy = factory.Create(Policy.LowerCase, args);
Assert.IsNotNull(policy);
}
Changed for the dictionary it looks like:
public void TestPolicyFactory()
{
var args = new PolicyArgs();
args.AdditionalParameters.Add("MinimumLength", 1);
var factory = new PolicyFactory();
var policy = factory.Create(Policy.LowerCase, args);
Assert.IsNotNull(policy);
}
Honestly you will get very opinionated answers to your questions so it may not fit stackoverflow rules.
My opinion:
I would give all my policies the same argument: a json in the form of a string. I would avoid inheritance because you will get into trouble to deserialize from your database into the correct type (database do not fit well with polymorphic collections).
once inside your class you can deserialize your json into your specific object and throw if it does not fit.
I wrote a class of account objects and hold a static List<T> of those account objects. My program loops through each account in the list, performing some work with the account, and then resetting at the top when it reaches the end of the list.
My issue is that I need to be able to reinsert the account into the list after my program finishes working with it, with some updated info added. Can I do this as written below, using the IndexOf() function to check for the object in the static list or will it fail because I added data to it? I don't understand which fields it compares to see if the two objects are the same.
Note: no duplicates are allowed in the list so there is no risk of updating the wrong item
public class Account
{
public string name;
public string password;
public string newInfo;
}
public static class Resources
{
private static List<Account> AccountList = new List<Account>();
private static int currentAccountIndex = 0;
public static Account GetNextAccount()
{
if (currentAccountIndex > AccountList.Count)
currentAccountIndex = 0;
return AccountList[currentAccountIndex++];
}
public static void UpdateAccount(Account account)
{
int index;
if ((index = AccountList.IndexOf(account)) >= 0)
AccountList[index] = account;
}
}
public class Program
{
public void PerformWork()
{
Account account = Resources.GetNextAccount();
// Do some work
account.newInfo = "foo";
Resources.UpdateAccount(account);
}
}
Another option is to use List.FindIndex, and pass a predicate. That is:
if ((index = AccountList.FindIndex(a => a.name == account.name)) >= 0)
AccountList[index] = account;
That way you can search on any arbitrary field or number of fields. This is especially useful if you don't have access to the source code for Account to add an overloaded Equals method.
One thing the accepted answer did not cover is you are supposed to override Equals(object) and GetHashCode() for IEquatable<T> to work correctly. Here is the full implementation (based off of keyboardP's answer)
public class Account : IEquatable<Account>
{
public string name;
public string password;
public string newInfo;
private readonly StringComparer comparer = StringComparer.OrdinalIgnoreCase;
public override bool Equals(object other)
{
//This casts the object to null if it is not a Account and calls the other Equals implementation.
return this.Equals(other as Account);
}
public override int GetHashCode()
{
return comparer.GetHashCode(this.newInfo)
}
public bool Equals(Account other)
{
//Choose what you want to consider as "equal" between Account objects
//for example, assuming newInfo is what you want to consider a match
//(regardless of case)
if (other == null)
return false;
return comparer.Equals(this.newInfo, other.newInfo);
}
}
Your object should implement the IEquatable interface and override the Equals method.
public class Account : IEquatable<Account>
{
public string name;
public string password;
public string newInfo;
public bool Equals(Account other)
{
//Choose what you want to consider as "equal" between Account objects
//for example, assuming newInfo is what you want to consider a match
//(regardless of case)
if (other == null)
return false;
return String.Equals(this.newInfo, other.newInfo,
StringComparison.OrdinalIgnoreCase);
}
}
If your class properly implements IEquatable<T>, then IndexOf() will use your Equals() method to test for equality.
Otherwise, IndexOf() will use reference equality.
You can use a custom Predicate for your class, such as:
public class Account
{
public string name;
public string password;
public string newInfo;
public class IndexOfName
{
private string _match = "";
public IndexOfName()
{
}
public Predicate<Account> Match(string match)
{
this._match = match;
return IsMatch;
}
private bool IsMatch(Account matchTo)
{
if (matchTo == null)
{
return false;
}
return matchTo.Equals(this._match);
}
}
}
Then you can use it as follow:
Account.IndexOf indexOf = new Account.IndexOf();
int index;
if ((index = AccountList.FindIndex(indexOf.Match("john"))) > 0)
{
// do something with John
}
if ((index = AccountList.FindIndex(indexOf.Match("jane"))) > 0)
{
// do something with Jane
}
You could even change the IndeOfName class to use a flag to switch between the type of info you are looking for. Ex: name or newInfo.
I have class which have too many related calculated properties.
I have currently kept all properties are read only.
some properties need long calculation and it is called again when its related properties are needed.
How can create this complex object .Also i want these properties should not be set from external code. I need show hide as i am binding properties for UI. Also i think order is also important.
My Class is something like
public string A
{
get
{
return complexMethod();
;
}
}
public string B
{
get
{
if (A == "value")
return "A";
else return "B";
;
}
}
public bool ShowHideA
{
get
{
return string.IsNullOrEmpty(A);
;
}
}
public bool ShowHideB
{
get
{
return string.IsNullOrEmpty(B);
;
}
}
public string complexMethod()
{
string value = "";
// calculation goes here
return value;
}
}
Thanks
You need to use Lazy type provided by .net:
Lazy<YourType> lazy = new Lazy<YourType>();
Make your properties internal to not be set from external code.
Well tall order isn't it?
One of the coolest things about extension methods is you can use types. This is perfect for writing external programs to calculate property values. Start like this...
public static class XMLibrary
{
public static MC CalculateValues(this MC myclass)
{
//for each property calculate the values here
if (myclass.Name == string.Empty) myclass.Name = "You must supply a name";
if (myclass.Next == 0) myclass.Next = 1;
//when done return the type
return myclass;
}
}
public class MC
{
public string Name { get; set; }
public int Next { get; set; }
}
public class SomeMainClass
{
public SomeMainClass()
{
var mc = new MC { Name = "test", Next = 0 };
var results = mc.CalculateValues();
}
}
There are many other ways to do class validation on a model, for example dataannotations comes to mind, or IValidatableObject works too. Keeping the validation separate from the class is a good idea.
//Complex properites are simple
public class MyComplextClass{
public List<MyThings> MyThings {get;set;}
public List<FileInfo> MyFiles {get;set;}
public List<DateTime> MyDates {get;set;}
}
I have following C# code. It works fine; but the GetDestination() method is cluttered with multiple if conditions by using is operator.
In .Net 4.0 (or greater) what is the best way to avoid these “if” conditions?
EDIT: Role is part of the business model, and the destination is purely an artifact of one particular application using that business model.
CODE
public class Role { }
public class Manager : Role { }
public class Accountant : Role { }
public class Attender : Role { }
public class Cleaner : Role { }
public class Security : Role { }
class Program
{
static string GetDestination(Role x)
{
string destination = #"\Home";
if (x is Manager)
{
destination = #"\ManagerHomeA";
}
if (x is Accountant)
{
destination = #"\AccountantHomeC";
}
if (x is Cleaner)
{
destination = #"\Cleaner";
}
return destination;
}
static void Main(string[] args)
{
string destination = GetDestination(new Accountant());
Console.WriteLine(destination);
Console.ReadLine();
}
}
REFERENCES
Dictionary<T,Delegate> with Delegates of different types: Cleaner, non string method names?
Jon Skeet: Making reflection fly and exploring delegates
if-else vs. switch vs. Dictionary of delegates
Dictionary with delegate or switch?
Expression and delegate in c#
Having virtual property which would be overriden in derived classes should do the trick:
class Role
{
public virtual string Destination { get { return "Home"; } }
}
class Manager : Role
{
public override string Destination { get { return "ManagerHome;"; } }
}
class Accountant : Role
{
public override string Destination { get { return "AccountantHome;"; } }
}
class Attender : Role
{
public override string Destination { get { return "AttenderHome;"; } }
}
class Cleaner : Role
{
public override string Destination { get { return "CleanerHome;"; } }
}
class Security : Role { }
I didn't make the property abstract, to provide default Home value when it's not overriden in derived class.
Usage:
string destination = (new Accountant()).Destination;
Console.WriteLine(destination);
Console.ReadLine();
Here's one option:
private static readonly Dictionary<Type, string> DestinationsByType =
new Dictionary<Type, string>
{
{ typeof(Manager), #"\ManagerHome" },
{ typeof(Accountant), #"\AccountantHome" },
// etc
};
private static string GetDestination(Role x)
{
string destination;
return DestinationsByType.TryGetValue(x.GetType(), out destination)
? destination : #"\Home";
}
Note:
This doesn't cope with null parameters. It's not clear whether or not you actually need it to. You can easily add null handling though.
This doesn't copy with inheritance (e.g. class Foo : Manager); you could do that by going up the inheritance hierarchy if necessary
Here's a version which does deal with both of those points, at the cost of complexity:
private static string GetDestination(Role x)
{
Type type = x == null ? null : x.GetType();
while (type != null)
{
string destination;
if (DestinationsByType.TryGetValue(x.GetType(), out destination))
{
return destination;
}
type = type.BaseType;
}
return #"\Home";
}
EDIT: It would be cleaner if Role itself had a Destination property. This could either be virtual, or provided by the Rolebase class.
However, it could be that the destination is really not something the Role should concern itself with - it could be that Role is part of the business model, and the destination is purely an artifact of one particular application using that business model. In that sort of situation, you shouldn't put it into Role, as that breaks separation of concerns.
Basically, we can't tell which solution is going to be most suitable without knowing more context - as is so often the way in matters of design.
Approach 1 (Selected): Using dynamic keyword to implement multimethods / double dispatch
Approach 2: Use a dictionary to avoid if blocks as mentioned in Jon Skeet’s answer below.
Approach 3: Use a HashList with delegates if there is condition other than equality (For example, if input < 25). Refer how to refactor a set of <= , >= if...else statements into a dictionary or something like that
Apporach 4: Virtual Functions as mentioned in MarcinJuraszek’s answer below.
MultiMethods / Double Dispatch approach using dynamic keyword
Rationale: Here the algorithm changes based on the type. That is, if the input is Accountant, the function to be executed is different than for Manager.
public static class DestinationHelper
{
public static string GetDestinationSepcificImplm(Manager x)
{
return #"\ManagerHome";
}
public static string GetDestinationSepcificImplm(Accountant x)
{
return #"\AccountantHome";
}
public static string GetDestinationSepcificImplm(Cleaner x)
{
return #"\CleanerHome";
}
}
class Program
{
static string GetDestination(Role x)
{
#region Other Common Works
//Do logging
//Other Business Activities
#endregion
string destination = String.Empty;
dynamic inputRole = x;
destination = DestinationHelper.GetDestinationSepcificImplm(inputRole);
return destination;
}
static void Main(string[] args)
{
string destination = GetDestination(new Security());
Console.WriteLine(destination);
Console.WriteLine("....");
Console.ReadLine();
}
}
This is a strongly typed, imperative language so if statements and type checking are going to happen.
Having said that, have you considered a virtual method on Role that can be overridden to provide a destination string?
A further alternative, a lookup table!
Dictionary<Type, string> paths = new Dictionary<TYpe, string>()
{
{ typeof(Manager), #"\ManagerHomeA" }
{ typeof(Accountant), #"\AccountantHomeC" }
{ typeof(Cleaner), "Cleaner" }
}
string path = #"\Home";
if(paths.ContainsKey(x.GetType())
path = paths[x];
One way to do it would be to use a map instead of an if:
//(psuedocode)
private Dictionary<Type, string> RoleMap;
void SomeInitializationCodeThatRunsOnce()
{
RoleMap.Add(typeof(Manager), #"\ManagerHome");
RollMap.Add(typeof(Accountant), #"\AccountantHome");
// ect...
}
string GetDestination(Role x)
{
string destination;
if(!RoleMap.TryGet(x.GetType(), out destination))
destination = #"\Home";
return destination;
}
Further reading: http://www.hanselman.com/blog/BackToBasicsMovingBeyondForIfAndSwitch.aspx
Role should have a virtual function that would return destination:
public virtual string GetDestination()
{
return "Home";
}
And all the classes should override this function and return the correct string. Then in the code you would have:
var role = new Accountant();
string destination = role.GetDestination();
I hope that helps. There may be typos, I am writing from head.
you can either use an interface definition or an abstract method / property
with interface:
public interface IDestinationProvider
{
sting Destination { get; }
}
string GetDestination(Role role)
{
var provider = role as IDestinationProvider;
if (provider != null)
return provider.Destination;
return "Default";
}
with an abstract base class
abstract class Role
{
public abstract string GetDestination();
}
class Manager : Role
{
public virtual string GetDestination() { return "ManagerHomeA"; }
}
string GetDestination(Role role)
{
return #"\" + role.GetDestination();
}
or with attributes:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class DestinationAttribute : Attribute
{
public DestinationAttribute() { this.Path = #"\Home"; }
public string Path { get; set; }
}
[Destination(Path = #"\ManagerHome")]
public class Manager : Role { }
string GetDestination(Role role)
{
var destination = role.GetType().GetCustomAttributes(typeof(DestinationAttribute), true).FirstOrDefault();
if (destination != null)
return destination.Path;
return #"\Home";
}
I have a AddCustomer() that has four parameters (firName, lastName, email, companyId), like below.
public class CustomerService
{
public bool AddCustomer(
string firName, string lastName,
string email, int companyId)
{
//logic: create company object based on companId
//other logic including validation
var customer = //create customer based on argument and company object
//save the customer
}
}
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Company Company { get; set; }
public string EmailAddress { get; set; }
//Other five primitive properties
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
}
My Question is that should the AddCustomer's parameter be changed to Customer object, like below, considering SOLID principle. Please note that only four fields shown above are used in the method.
public bool AddCustomer(Customer customer){
}
Update
If below is used:
public bool AddCustomer(Customer customer)
The issue: One of the parameter is CompanyId. Thus, creating a Customer constructor with a CompanyId as parameter might not work on all circumstances. However, without constructor, it would be confusing for AdCustomer()'s client as to what properties to assign.
Update 2
Ideally, i would like to protect invariant of entities Customer and Company by restricting property setters.
An answer very much depends on what the purpose and the responsibility of the CustomerService class and the Customer class is, and what they are intended to achieve.
From your question it would seem ("other logic including validation") that it is the responsibility of CustomerService to determine what constitutes a valid new Customer to be registered, whereas the Customer class itself is nothing more than a DTO without any behavior.
So consider the following hypothetical use cases: a customer's email changes; the Company the Customer works for changes; if the Company is bankrupt, the new Customer registration should be refused; if the Company produces a lot of sales for us, the Customer should be regarded as a Premium Customer. How would such cases be handled and what responsibilities are involved?
You might want to approach this differently, in the sense that you make both intent and behavior explicit, instead of having "AddCustomer", "UpdateCustomer", "DeleteCustomer" and "GetCustomer(Id)". The Customer service could be responsible for service coordination and infrastructure aspects, while the Customer class really focuses on the required domain behavior and customer related business rules.
I will outline one (a CQRS type approach) of several possible approaches to better break up responsibilities, to illustrate this:
Encode behavioral intent and decisions as Commands and Events respectively.
namespace CustomerDomain.Commands
{
public class RegisterNewCustomer : ICommand
{
public RegisterNewCustomer(Guid registrationId, string firstName, string lastName, string email, int worksForCompanyId)
{
this.RegistrationId = registrationId;
this.FirstName = firstName;
// ... more fields
}
public readonly Guid RegistrationId;
public readonly string FirstName;
// ... more fields
}
public class ChangeCustomerEmail : ICommand
{
public ChangeCustomerEmail(int customerId, string newEmail)
// ...
}
public class ChangeCustomerCompany : ICommand
{
public ChangeCustomerCompany(int customerId, int newCompanyId)
// ...
}
// ... more commands
}
namespace CustomerDomain.Events
{
public class NewCustomerWasRegistered : IEvent
{
public NewCustomerWasRegistered(Guid registrationId, int assignedId, bool isPremiumCustomer, string firstName /* ... other fields */)
{
this.RegistrationId = registrationId;
// ...
}
public readonly Guid RegistrationId;
public readonly int AssignedCustomerId;
public readonly bool IsPremiumCustomer;
public readonly string FirstName;
// ...
}
public class CustomerRegistrationWasRefused : IEvent
{
public CustomerRegistrationWasRefused(Guid registrationId, string reason)
// ...
}
public class CustomerEmailWasChanged : IEvent
public class CustomerCompanyWasChanged : IEvent
public class CustomerWasAwardedPremiumStatus : IEvent
public class CustomerPremiumStatusWasRevoked : IEvent
}
This allows expressing intent very clearly, and including only the information that is actually needed to accomplish a specific task.
Use small and dedicated services to deal with the needs of your application domain in making decisions:
namespace CompanyIntelligenceServices
{
public interface ICompanyIntelligenceService
{
CompanyIntelligenceReport GetIntelligenceReport(int companyId);
// ... other relevant methods.
}
public class CompanyIntelligenceReport
{
public readonly string CompanyName;
public readonly double AccumulatedSales;
public readonly double LastQuarterSales;
public readonly bool IsBankrupt;
// etc.
}
}
Have the CustomerService implementation deal with infrastructure / coordination concerns:
public class CustomerDomainService : IDomainService
{
private readonly Func<int> _customerIdGenerator;
private readonly Dictionary<Type, Func<ICommand, IEnumerable<IEvent>>> _commandHandlers;
private readonly Dictionary<int, List<IEvent>> _dataBase;
private readonly IEventChannel _eventsChannel;
private readonly ICompanyIntelligenceService _companyIntelligenceService;
public CustomerDomainService(ICompanyIntelligenceService companyIntelligenceService, IEventChannel eventsChannel)
{
// mock database.
var id = 1;
_customerIdGenerator = () => id++;
_dataBase = new Dictionary<int, List<IEvent>>();
// external services and infrastructure.
_companyIntelligenceService = companyIntelligenceService;
_eventsChannel = eventsChannel;
// command handler wiring.
_commandHandlers = new Dictionary<Type,Func<ICommand,IEnumerable<IEvent>>>();
SetHandlerFor<RegisterNewCustomerCommand>(cmd => HandleCommandFor(-1,
(id, cust) => cust.Register(id, cmd, ReportFor(cmd.WorksForCompanyId))));
SetHandlerFor<ChangeCustomerEmail>(cmd => HandleCommandFor(cmd.CustomerId,
(id, cust) => cust.ChangeEmail(cmd.NewEmail)));
SetHandlerFor<ChangeCustomerCompany>(cmd => HandleCommandFor(cmd.CustomerId,
(id, cust) => cust.ChangeCompany(cmd.NewCompanyId, ReportFor(cmd.NewCompanyId))));
}
public void PerformCommand(ICommand cmd)
{
var commandHandler = _commandHandlers[cmd.GetType()];
var resultingEvents = commandHandler(cmd);
foreach (var evt in resultingEvents)
_eventsChannel.Publish(evt);
}
private IEnumerable<IEvent> HandleCommandFor(int customerId, Func<int, Customer, IEnumerable<IEvent>> handler)
{
if (customerId <= 0)
customerId = _customerIdGenerator();
var events = handler(LoadCustomer(customerId));
SaveCustomer(customerId, events);
return events;
}
private void SetHandlerFor<TCommand>(Func<TCommand, IEnumerable<IEvent>> handler)
{
_commandHandlers[typeof(TCommand)] = cmd => handler((TCommand)cmd);
}
private CompanyIntelligenceReport ReportFor(int companyId)
{
return _companyIntelligenceService.GetIntelligenceReport(companyId);
}
private Customer LoadCustomer(int customerId)
{
var currentHistoricalEvents = new List<IEvent>();
_dataBase.TryGetValue(customerId, out currentHistoricalEvents);
return new Customer(currentHistoricalEvents);
}
private void SaveCustomer(int customerId, IEnumerable<IEvent> newEvents)
{
List<IEvent> currentEventHistory;
if (!_dataBase.TryGetValue(customerId, out currentEventHistory))
_dataBase[customerId] = currentEventHistory = new List<IEvent>();
currentEventHistory.AddRange(newEvents);
}
}
And then that allows you to really focus on the required behavior, business rules and decisions for the Customer class, maintaining only the state needed to perform decisions.
internal class Customer
{
private int _id;
private bool _isRegistered;
private bool _isPremium;
private bool _canOrderProducts;
public Customer(IEnumerable<IEvent> eventHistory)
{
foreach (var evt in eventHistory)
ApplyEvent(evt);
}
public IEnumerable<IEvent> Register(int id, RegisterNewCustomerCommand cmd, CompanyIntelligenceReport report)
{
if (report.IsBankrupt)
yield return ApplyEvent(new CustomerRegistrationWasRefused(cmd.RegistrationId, "Customer's company is bankrupt"));
var isPremium = IsPremiumCompany(report);
yield return ApplyEvent(new NewCustomerWasRegistered(cmd.RegistrationId, id, isPremium, cmd.FirstName, cmd.LastName, cmd.Email, cmd.WorksForCompanyID));
}
public IEnumerable<IEvent> ChangeEmail(string newEmailAddress)
{
EnsureIsRegistered("change email");
yield return ApplyEvent(new CustomerEmailWasChanged(_id, newEmailAddress));
}
public IEnumerable<IEvent> ChangeCompany(int newCompanyId, CompanyIntelligenceReport report)
{
EnsureIsRegistered("change company");
var isPremiumCompany = IsPremiumCompany(report);
if (!_isPremium && isPremiumCompany)
yield return ApplyEvent(new CustomerWasAwardedPremiumStatus(_id));
else
{
if (_isPremium && !isPremiumCompany)
yield return ApplyEvent(new CustomerPremiumStatusRevoked(_id, "Customer changed workplace to a non-premium company"));
if (report.IsBankrupt)
yield return ApplyEvent(new CustomerLostBuyingCapability(_id, "Customer changed workplace to a bankrupt company"));
}
}
// ... handlers for other commands
private bool IsPremiumCompany(CompanyIntelligenceReport report)
{
return !report.IsBankrupt &&
(report.AccumulatedSales > 1000000 || report.LastQuarterSales > 10000);
}
private void EnsureIsRegistered(string forAction)
{
if (_isRegistered)
throw new DomainException(string.Format("Cannot {0} for an unregistered customer", forAction));
}
private IEvent ApplyEvent(IEvent evt)
{
// build up only the status needed to take domain/business decisions.
// instead of if/then/else, event hander wiring could be used.
if (evt is NewCustomerWasRegistered)
{
_isPremium = evt.IsPremiumCustomer;
_isRegistered = true;
_canOrderProducts = true;
}
else if (evt is CustomerRegistrationWasRefused)
_isRegistered = false;
else if (evt is CustomerWasAwardedPremiumStatus)
_isPremium = true;
else if (evt is CustomerPremiumStatusRevoked)
_isPremium = false;
else if (evt is CustomerLostBuyingCapability)
_canOrderProducts = false;
return evt;
}
}
An added benefit is that the Customer class in this case is completely isolated from any infrastructure concerns can be easily tested for correct behavior and the customer domain module can be easily changed or extended to accommodate new requirements without breaking existing clients.
yes.... if its valid to create a customer with those 4 properties.... ideally you'd have a constructor with those 4. that way the create responsibility lives with the customer object and Customer Service doesn't need to know about it, it just deals with "Customers".
How about using the builder pattern resulting in code somewhat like this:
var customer = new CustomerBuilder()
.firstName("John")
.lastName("Doe")
.email("john.doe#example.com")
.companyId(6)
.createCustomer();
customerService.AddCustomer(customer);
Then you can have your builder class handle looking up company objects when createCustomer is called and the order of parameters no longer matters and you have a convenient place to put logic to choose sensible defaults.
This also gives you a convenient location for validation logic so you can't get an invalid instance of Customer in the first place.
Or another possible way would be to have AddCustomer return a command object so your client code could do this:
customerService.AddCustomer()
.firstName("John")
.lastName("Doe")
.email("john.doe#example.com")
.companyId(6)
.execute();