NDepend

Improve your .NET code quality with NDepend

Clean Architecture C# — an example

Starting A Clean Architecture Example in C#

It’s time for the second part of our series about clean architecture. As promised in the first post, we’re going to show you a sample application in C#, to demonstrate what a clean architecture implementation might look like.

Even though our sample application will be minimalist, it’s still too much for a single post. We want to keep things light and easy for you, so we’ll have to break this post into two or three parts.

Don’t worry though: at the end, I’ll connect all the dots and things should (hopefully) make sense. And all the generated code will be publicly available for you to download and study on your own.

A Clean Architecture Sample: Choosing an Application

First things first. We have to decide what kind of application we’re going to write. The ideal app has to meet the following two criteria:

  • It has to be very simple, for ease of understanding and to make sure it doesn’t take a ton of time.
  • It has to be business-logic-y. In other words, it can’t be just a CRUD.

The application used for the example will be a very simple todo list app. Yeah, I know. It’s probably the most overused example in the history of blogs and side projects, but I think it meets the criteria. So, todo list it is.

Defining the Use Cases

In several talks he’s given over the years, Robert C. Martin, a.k.a. Uncle Bob, talks about he calls “interactors.” An interactor is an object that encapsulates a single “task”—and I use this word very loosely here—that a user can perform using the application.

Fortunately, in the seminal blog post about clean architecture, he appears to have abandoned the name “interactor” in favor of “use case.” I’ve yet to read his new book (called, unsurprisingly, Clean Architecture), so I don’t know current names of various clean architecture components right now.

So, as I’ve just mentioned, an interactor or use case encapsulates a single “thing”—or “action,” as I like to put it—that a user can perform on the application. I really like the term “use case” since I think it fits nicely with the concept of user story from extreme programming. So, what would be the first user stories? I guess they could be something like this:

  • A user can add a new task.
  • The task must have a title, which can’t be an empty string.
  • The task must also have a due date and hour, which must be after now.
  • A user can postpone a task by any positive number of days.
  • A user can complete a task.
    • There is no way to un-complete a task.
    • After completed, a task can’t be postponed.

After analyzing the list above, we could have come up with the following use cases:

  • AddTask
  • CompleteTask
  • PostponeTask

In a real application, there would probably be a lot more. But for our purposes here, it makes sense to keep the number of use cases as low as possible.

Starting the Application

Let’s fire up Visual Studio 2017 and start coding. Start a new solution of type “ClassLibrary,” like the following image:

Now, let’s do the following:

  • Delete the default “Class1.cs” class.
  • Rename the default project to “CleanArchitectureSample.UseCases.”
  • Rename the default namespace of the project to “carlosschults.CleanArchitectureSample.UseCases.”

And now it’s time to start coding the first use case (“AddTask”). This will be an object with the following properties:

  • It will receive all it needs to perform its job by its constructor.
  • It will have just one method, which we can call “Handle,” “Run,” or “Execute.”
  • This method will return a kind of result type, meant to inform the calling parties about the result of the task.

Right-click on the project and go to “Add -> Class…” When you’re prompted for a name, type “AddTask.” As soon as the class is created, paste the following text on it:

After doing that, you’ll see a lot of errors since this code references a lot of things that don’t exist (yet). So, go to each error, hover the cursor above it, and when the “quick corrections” icon shows up, click on it and accept the “Generate class/interface ‘X'” suggestion.

What have we done so far? We’ve defined our “AddTask” use case class. It receives three parameters:

  • request, of type “AddTaskRequest.” This is supposed to be a very simple data structure that just captures the data supplied by the user.
  • repository, of type “ITaskRepository,” which abstracts persistence for our entity.
  • clock, of type “IClock,” which abstracts over access to current time and date, which is particularly relevant for testing purposes.

The class’s constructor checks each argument for null and then assigns them to private fields.

With the required dependencies in place, it’s time for us to implement the main method in the class, called “Execute.” Copy and paste the following code to your class:

As in the previous copied and pasted code, this will generate some errors. And we aren’t very fond of errors. So we’re going to do the “Generate class ‘x'” routine all over again, except for the “Task” class. Why, you ask? Well, this class is special. It’s going to require its own project since it’s an entity.

Enter the Entities

Now it’s time to create a new project, which will represent the central layer depicted in the clean architecture diagram. Robert C. Martin calls this layer simply “Entities.” I’m going to name the project “Domain,” though, and the reason for that is twofold:

  • First, it’s a nod to domain-driven design, as in Eric Evans’s famous book.
  • Secondly, the project might house objects beyond entities (e.g., value objects, for instance).

Let’s get to work. Right-click on the solution, “Add new project.” Nothing new here: the project will be of the type “Class Library,” and it’ll be called “CleanArchitectureSamples.Domain.” After creating the project, delete the default class and customize the default namespace, following the first project’s example.

Finally, let’s create the “Task” class. A very basic initial implementation could be as follows:

Here’s where my implementation might start surprising you. First, I kept the setters for the properties private. So, the class itself isn’t immutable, but its properties can’t be changed freely from the outside of the class.

The other important thing here isn’t just a bunch of properties. In the spirit of a rich domain model, it actually has methods that implement domain business rules and perform validations. For instance, the postpone method throws if the task is already completed. It also throws when the given number of days if less then or equal to zero.

That’s All for Today, Folks!

That’s the first part of our “Clean Architecture Example In C#” series, which is itself part of a larger series about the whole concept of clean architecture. It all started with the previous post, in which we laid out the foundations and explained what clean architecture is, what its benefits are, and why you should probably apply it to your projects.

Then, in this “inner” series we started today, we’ll show you an extremely simple yet practical example of clean architecture in action.

Finally, we hope to wrap it up by showing how NDepend rules can help you achieve an even cleaner architecture.

Contributing Author

Carlos Schults is a .NET software developer with experience in both desktop and web development, and he’s now trying his hand at mobile. He has a passion for writing clean and concise code, and he’s interested in practices that help you improve app health, such as code review, automated testing, and continuous build. You can read more from Carlos at carlosschults.net.

Comments:

  1. Nice post! Thx for sharing your thoughts on how to implement Uncle Bob’s Clean Architecture.

    Once question: Why have you chosen to pass the request object through the constructor as well? i would have thought passing it via Execute() would be more natural …

    Btw: Interestingly I have started my blog series about “Implementing Clean Architecture” also with describing use cases (after short intro) – https://plainionist.github.io/Implementing-Clean-Architecture-UseCases/ – if you have some time i would be happy about feedback – thx!

  2. Hi, thanks for your comment.

    So, I’d say it was mostly an arbitrary decision. I could’ve done the way you say, and it would make sense too.

    I’ll check out your post soon. Thanks again!

Leave a Reply

Your email address will not be published. Required fields are marked *