Using Use Cases in Code
Want to blow the minds of your fellow developers with a powerful technique in software development that almost nobody is using? Then please read on.
I am not exaggerating; in my 25 years in software development, I have seen almost no developer do this. I admit this could be an aberration of my limited experience. However, I think that there is something to be had here.
I am talking about using Use Cases classes in source code.
Last time, in The Case for Using Use Cases in Code, I laid out the claim for why developers should be using them.
Today we’ll get our hands dirty; We’ll look at the code for a use case class, RegisterCustomerUseCase. As its name suggests, the underlying business requirement is the common one of registering new customers in a system.
We’ll see how this use case class plugs into a Web API project. And I’ll explain how we could readily plug it into another type of application project, say a console app project.
Example: Registering a new Customer
WHAT might the business requirements be for registering a new customer?
Let’s assume
- We need customer first name, last name and email address.
- The email address must not be already in the system.
- Assign the new customer an independent identifier,
- Save the customer in the system.
OK, so HOW might these business requirements be fulfilled?
- Say, the Business Owner has decided to expose this functionality via a web API so multiple different clients can consume it; for example, a mobile app or a web application.
Ideally, in code, these two separate requirements, the WHAT and the HOW are also kept separate. In our example, the RegisterCustomerUseCase, below, addresses the WHAT component.
Note: The companion code is on GitHub. (Unit tests verifying the use case and web API behaviour are included.)
Disclaimer: I love C#. Therefore most of my code examples are in C#. With minor language-specific modifications, most if not all of the shown C# code will also apply to other object-oriented languages like Java & C++.
Business Logic
Below is the listing of the RegisterCustomerUseCase class:
As you can see, the high-level work happens in the use case’s public Register() method. The code is so simple, it can (almost) be read and understood by a layperson:
- Basic Validation — Validate the CustomerRegistration input data. Checks that it’s not null. Further validation delegated to CustomerRegistration — validates the presence of first name, last name and email address.
- Advanced Validation — Try to retrieve customer by email address from customer repository (i.e. database). If a customer with the given email address already exists, then throw an exception. (Learn when and how to use runtime exceptions here and here.)
- Type Conversion — Convert the CustomerRegistration to a Customer. Wouldn’t it be easier if we passed in a Customer to Register()? As an analogy, I like to think of it this way; when I apply for life insurance cover, I do this with an application form. Once received, my insurer will submit my application to their back-office validation procedures. If it goes well, and my application passes my insurer’s internal processes, they will convert my application into a policy. In our case, we are receiving a CustomerRegistration and turning it into a Customer instance once validated.
The more our systems replicate the behaviour and naming of our business processes, the less likely there will be misunderstandings between the business and developers.
- Persistence — Finally, save the newly created Customer object to the repository and return it (including new customer Id) to the caller of the Use Case’s Register() method.
At the highest level, these are the business requirements specified as code!
A few salient points:
- Pure Business Logic — This use case class, in fact, all code in the BusinessLogic project, contains business logic only, pure and simple. There are no references or even mentions of anything web, mobile, database technologies, connection strings. Zip, Zilch, Zero, Nada, Nothing.
- Delivery Medium Plugability — If there were such references (to web, SQL, mobile), it would no longer be just business logic. It would lose the ability to transfer between delivery media; say from web service to Windows Desktop application.
- Data Plugability — Dependency on the customer database has been abstracted away via the ICustomerRepository interface. This abstraction allows for the plugging in of any database technology, be it MongoDB, SQL, file system, whatever. To illustrate the point, I have included a simplistic in-memory implementation of ICustomerRepository, that will only retain customers until the hosting application goes out of memory. My point is that a real persistence technology would be backing code implementing ICustomerRepository.
- Business Logic First — I wrote the use case class and business logic before the web API or database code. Why? Two reasons: 1. WHAT the application does is more stable than HOW it does it. For example, business stakeholders might decide to move from a web client to a mobile client. However, that a customer gets registered (WHAT the system does) is not changing. 2. Starting development with web or the database, rather than business logic, is more likely to create inadvertent couplings between these things and the business logic. Best avoided.
- Decoupling and Delegation — Our RegisterCustomerUseCase class delegates responsibilities to other business logic entities, like CustomerRegistration and Customer entities. Furthermore, data service implementations are decoupled via abstractions, such as the ICustomerRegistration interface.
Code for the CustomerRegistration (detailed validation) and the Customer (creates Id), below:
Use Case In Web Api
Let’s look at an example of RegisterCustomerUseCase usage. As part of the companion code on Github, there is a WebApi project. You do not have to know about technology specifics, i.e. ASP.NET WebApi, to understand the following code — the salient points should be easy to grasp.
The Register() method on the CustomerController (in the Controllers folder) is automatically invoked when the webserver receives an HTTP POST request.
What happens here? The code is concise, lending clarity and readability:
- Validation — Validate the incoming web API-specific customer registration model.
- Conversion — Convert this model to one that the use case class recognises. (Remember, to stay independent of any particular delivery medium, even a simple data transfer object used by the web API must not be known to the BusinessLogic project. A conversion from the ApiCustomerRegistration type to the CustomerRegistration class must take place.)
- Business Logic — Invoke the use case.
- Success — If it all went as expected, return the customer from the use case as an HTTP response with status code 200 — OK.
- Error — On the other hand, if an error occurred, and it’s contained in an exception, step into the catch block. Call the HandleException() method and, depending on the exception base type, return a 400 — Bad Request, or a 500 — Internal Server Error.
OK, that’s a wrap. We covered off heaps today. IMHO you’ll derive the most value from cloning the GitHub code and take a closer look at it. The WebApi project should be in a runnable state (in Visual Studio 2017). It will also be instructive to examine the accompanying unit tests for the business logic code and the Web Api’s controller action code.
Conclusion
We looked at code for a use case class that registers new customers in a system. We saw that business logic modules should only contain pure business logic. If they comply with this guideline, then their use case classes will be reusable across different delivery media. We also examined code for a web API project that had this use class plugged into it.
If you enjoyed this article, please leave some claps — and a bunch of claps if you loved it! :) Thank you kindly.
Join my email list to fast-track your software engineering career.
When signing up, you’ll get my guide, ‘The Road to Master Progammer’, containing 3 powerful ideas to help you shorten your journey to expert programmer.