From 213a9d17df1b7b08d9694a96cfe6f3168fed0d8e Mon Sep 17 00:00:00 2001 From: Shaibal Ghosh Date: Fri, 3 Feb 2023 22:20:38 +0530 Subject: [PATCH 1/3] Added self delegation functions in Customer entity Addinging self delegation can help adding extra logic before we set the variable. We can add the same extra logic in property set value too but it looks cleaner to have a separate function. For example, before setting email, we can check the format. --- .../Customers/Customer.cs | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/SampleProject.Domain/Customers/Customer.cs b/src/SampleProject.Domain/Customers/Customer.cs index 14bbe6e..4be88cd 100644 --- a/src/SampleProject.Domain/Customers/Customer.cs +++ b/src/SampleProject.Domain/Customers/Customer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using SampleProject.Domain.Customers.Orders; using SampleProject.Domain.Customers.Orders.Events; @@ -30,9 +31,9 @@ private Customer() private Customer(string email, string name) { this.Id = new CustomerId(Guid.NewGuid()); - _email = email; - _name = name; - _welcomeEmailWasSent = false; + SetCustomerEmail(email); + SetCustomerName(name); + SetWelcomeEmailSentStatus(false); _orders = new List(); this.AddDomainEvent(new CustomerRegisteredEvent(this.Id)); @@ -91,7 +92,32 @@ public void RemoveOrder(OrderId orderId) public void MarkAsWelcomedByEmail() { - this._welcomeEmailWasSent = true; + SetWelcomeEmailSentStatus(true); + } + + private void SetCustomerEmail(string email) + { + if (!IsValidEmailFormat(email)) + { + throw new InvalidDataException(); + } + _email = email; + } + + private bool IsValidEmailFormat(string email) + { + //TODO: Implement email format verification logic + return true; + } + + private void SetCustomerName(string name) + { + _name = name; + } + + private void SetWelcomeEmailSentStatus(bool wasSent) + { + _welcomeEmailWasSent = wasSent; } } } \ No newline at end of file From a186eeb5bc804658cfb57e6de1838d97aaa6fc52 Mon Sep 17 00:00:00 2001 From: Shaibal Ghosh Date: Sat, 4 Feb 2023 02:12:16 +0530 Subject: [PATCH 2/3] Refactored customer Entity 1 - Added Unique email rule violation exception catch and return null in case of violation 2 - Added email format validation rule checker --- .../DomainServices/CustomerEmailChecker.cs | 32 ++++++++++++++++ .../Customers/Customer.cs | 38 +++++++++++++------ .../Customers/ICustomerEmailChecker.cs | 7 ++++ .../Customers/Rules/CustomerEmailValidRule.cs | 27 +++++++++++++ .../Domain/DomainModule.cs | 4 ++ 5 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 src/SampleProject.Application/Customers/DomainServices/CustomerEmailChecker.cs create mode 100644 src/SampleProject.Domain/Customers/ICustomerEmailChecker.cs create mode 100644 src/SampleProject.Domain/Customers/Rules/CustomerEmailValidRule.cs diff --git a/src/SampleProject.Application/Customers/DomainServices/CustomerEmailChecker.cs b/src/SampleProject.Application/Customers/DomainServices/CustomerEmailChecker.cs new file mode 100644 index 0000000..800c791 --- /dev/null +++ b/src/SampleProject.Application/Customers/DomainServices/CustomerEmailChecker.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Net.Mail; +using System.Text; +using SampleProject.Domain.Customers; + +namespace SampleProject.Application.Customers.DomainServices +{ + class CustomerEmailChecker : ICustomerEmailChecker + { + private readonly string _customerEmail; + + public CustomerEmailChecker(string customerEmail) + { + _customerEmail = customerEmail; + } + + public bool IsValid(string customerEmail) + { + try + { + var emailAddress = new MailAddress(customerEmail); + } + catch + { + return false; + } + + return true; + } + } +} diff --git a/src/SampleProject.Domain/Customers/Customer.cs b/src/SampleProject.Domain/Customers/Customer.cs index 4be88cd..1dfd82a 100644 --- a/src/SampleProject.Domain/Customers/Customer.cs +++ b/src/SampleProject.Domain/Customers/Customer.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Linq.Expressions; +using System.Net.Mail; using SampleProject.Domain.Customers.Orders; using SampleProject.Domain.Customers.Orders.Events; using SampleProject.Domain.Customers.Rules; @@ -42,10 +44,32 @@ private Customer(string email, string name) public static Customer CreateRegistered( string email, string name, - ICustomerUniquenessChecker customerUniquenessChecker) + ICustomerUniquenessChecker customerUniquenessChecker, + ICustomerEmailChecker customerEmailChecker = null) { - CheckRule(new CustomerEmailMustBeUniqueRule(customerUniquenessChecker, email)); + try + { + if (customerEmailChecker != null) + { + CheckRule(new CustomerEmailValidRule(customerEmailChecker, email)); + } + } + catch (BusinessRuleValidationException ex) + { + return null; + } + try + { + + CheckRule(new CustomerEmailMustBeUniqueRule(customerUniquenessChecker, email)); + } + + catch(BusinessRuleValidationException ex) + { + //FIXME: Maybe throw duplicate email exception? Or log the error? + return null; + } return new Customer(email, name); } @@ -97,19 +121,9 @@ public void MarkAsWelcomedByEmail() private void SetCustomerEmail(string email) { - if (!IsValidEmailFormat(email)) - { - throw new InvalidDataException(); - } _email = email; } - private bool IsValidEmailFormat(string email) - { - //TODO: Implement email format verification logic - return true; - } - private void SetCustomerName(string name) { _name = name; diff --git a/src/SampleProject.Domain/Customers/ICustomerEmailChecker.cs b/src/SampleProject.Domain/Customers/ICustomerEmailChecker.cs new file mode 100644 index 0000000..7e5d833 --- /dev/null +++ b/src/SampleProject.Domain/Customers/ICustomerEmailChecker.cs @@ -0,0 +1,7 @@ +namespace SampleProject.Domain.Customers +{ + public interface ICustomerEmailChecker + { + bool IsValid(string customerEmail); + } +} \ No newline at end of file diff --git a/src/SampleProject.Domain/Customers/Rules/CustomerEmailValidRule.cs b/src/SampleProject.Domain/Customers/Rules/CustomerEmailValidRule.cs new file mode 100644 index 0000000..e226a5b --- /dev/null +++ b/src/SampleProject.Domain/Customers/Rules/CustomerEmailValidRule.cs @@ -0,0 +1,27 @@ +using SampleProject.Domain.SeedWork; + +namespace SampleProject.Domain.Customers.Rules +{ + internal class CustomerEmailValidRule : IBusinessRule + { + private readonly ICustomerEmailChecker _customerEmailChecker; + + private readonly string _email; + + public CustomerEmailValidRule( + ICustomerEmailChecker customerEmailChecker, + string email) + { + _customerEmailChecker = customerEmailChecker; + _email = email; + } + + public bool IsBroken() + { + return !_customerEmailChecker.IsValid(_email); + } + + + public string Message => "Customer with this email already exists."; + } +} \ No newline at end of file diff --git a/src/SampleProject.Infrastructure/Domain/DomainModule.cs b/src/SampleProject.Infrastructure/Domain/DomainModule.cs index 892eb4e..4b09c4f 100644 --- a/src/SampleProject.Infrastructure/Domain/DomainModule.cs +++ b/src/SampleProject.Infrastructure/Domain/DomainModule.cs @@ -14,6 +14,10 @@ protected override void Load(ContainerBuilder builder) .As() .InstancePerLifetimeScope(); + builder.RegisterType() + .As() + .InstancePerLifetimeScope(); + builder.RegisterType() .As() .InstancePerLifetimeScope(); From 6eafa344917b4064796afdac5e1ba051477649ca Mon Sep 17 00:00:00 2001 From: Shaibal Ghosh Date: Sat, 4 Feb 2023 10:25:25 +0530 Subject: [PATCH 3/3] Sending email rule checker object while creating customer object --- .../RegisterCustomerCommandHandler.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/SampleProject.Application/Customers/RegisterCustomer/RegisterCustomerCommandHandler.cs b/src/SampleProject.Application/Customers/RegisterCustomer/RegisterCustomerCommandHandler.cs index 4dee03c..2bdf419 100644 --- a/src/SampleProject.Application/Customers/RegisterCustomer/RegisterCustomerCommandHandler.cs +++ b/src/SampleProject.Application/Customers/RegisterCustomer/RegisterCustomerCommandHandler.cs @@ -13,20 +13,29 @@ public class RegisterCustomerCommandHandler : ICommandHandler Handle(RegisterCustomerCommand request, CancellationToken cancellationToken) { - var customer = Customer.CreateRegistered(request.Email, request.Name, this._customerUniquenessChecker); + var customer = Customer.CreateRegistered(request.Email, request.Name, this._customerUniquenessChecker, this._customerEmailChecker); + + if (customer == null) + { + // What is the best way to return error to user? + return new CustomerDto(); + } await this._customerRepository.AddAsync(customer);