How To Partition A System (Part 1)
This week we’ll undertake a short series on how to subdivide and structure our classes and programs. Hopefully, I will be able to communicate my thinking process clearly so that you can use it too. It’s not going to be easy – I’ll be trying to condense an approach into a few sessions that took me 25 years to master. However, if I am successful, you will have a uniquely valuable instrument in your programming toolbox.
Today’s article is also available on YouTube.
Ok, let’s look at some code:
public class RegisterNewCustomerUseCase { public Customer Register(CustomerRegistration registration) { Validate(registration); var customer = registration.ToCustomer(); Repository.SaveCustomer(customer); return customer; } // ... }
What we have here is a class for registering a new customer in our system. It’s an example of a high-level business logic class – a use case class. Use cases only manage operational workflow and do not concern themselves with low-level details like UI, web, mobile, database and remote service calls.
The Register() method ensures that as part of registering the customer we
- Validate the input data, then
- we convert the input registration data into a new customer, then
- we save the new customer into our system, and finally
- we return the new customer.
Changing Requirements
As so often happens, requirements change. The business thinks that it would be friendly if new customers received a warm welcome email. Here is where we should make the change in Register():
public Customer Register(CustomerRegistration registration) { Validate(registration); var customer = registration.ToCustomer(); Repository.SaveCustomer(customer); // Send the new customer a Welcome email. return customer; }
Another developer has implemented the Welcome email functionality. It’s up to us to review their code:
public Customer Register(CustomerRegistration registration) { Validate(registration); var customer = registration.ToCustomer(); Repository.SaveCustomer(customer); // *** START - Send the new customer a Welcome email *** StringBuilder builder = new StringBuilder(); builder.AppendLine($"Hi {customer.FirstName},"); builder.AppendLine(); builder.AppendLine("Thank you for signing up to our newsletter."); builder.AppendLine(); builder.AppendLine("Yours sincerely,"); builder.AppendLine("The ABC Finance Customer Services Team"); string welcomeEmailBody = builder.ToString(); string welcomeEmailSubject = "Welcome to ABC Finance!"; MailMessage email = new MailMessage("info@abcfinance.example.com", customer.EmailAddress, welcomeEmailSubject, welcomeEmailBody); var smtpHost = ConfigurationManager.AppSettings["SmtpHost"]; SmtpClient smtpClient = new SmtpClient(smtpHost); smtpClient.Send(email); // *** END - Send the new customer a Welcome email *** return customer; }
Are there any problems with this code? What do you think?
Note: I recommend you take a few minutes to review the code before you read on. I believe you will gain a deeper understanding if you try this by yourself first.
OK, here are the problems I have identified:
- Mixing of abstraction levels – Use cases manage high-level responsibilities. Yet the customer Welcome email formatting and sending is available in all its glory. Surely this has nothing to do with the high-level workflow. It feels wrong to have this much detail here.
- Changing email sender, subject and body – The business will likely want to fine-tune the text of the welcome email. Do we want to recompile and redeploy whenever these changes occur? Probably not.
- Different emailing mechanisms – This code is using SmtpClient for sending email via an SMTP server. However, what if we wanted to send emails in future via Amazon Web Services Simple Email Service (AWS SES)? It feels like we wouldn’t be able to achieve this without making significant changes in Register(). Is it possible for us to change the emailing mechanism in a pluggable manner?
- Different notification mechanisms – What if we wanted to deliver welcome messages via media other than email? Eg. SMS/txt or in-app? Surely this is another foreseeable change that we should be able to satisfy without modifications to our RegisterNewCustomerUseCase class. Right now switching to SMS/txt would require even more significant changes than moving to AWS SES for sending emails: The message format would change since SMS/txt messages are more concise than emails.
It feels as if not all is well with our customer Welcome email implementation. It’s not compliant with the Open-Closed Principle, which asks that we construct our systems in such a way that we can write new code rather than change existing code.
Continue with How To Partition A System (Part 2 – The 3 Buckets)
Leave a Reply
Want to join the discussion?Feel free to contribute!