architecture

A Simple Organization for ASP.NET Web API Producer/Consumer Apps

My team regularly writes ASP.NET Web API projects which have multiple consumers, and oftentimes we also write at least one of said consumers. To accomplish this, we often invoke a project layout and architecture that doesn't seem terribly obvious to newcomers to my group. I hope to better explain myself as to why this project organization works in this post (so I can just say, "Hey, new guy, lookie here!" and point them at this post.)

Sample Architecture

So, let's get the spoilers out of the way first: this architecture works because both the API and the MVC app use a common library of DTO objects. In this way, whenever an object being returned from the API is deserialized from its JSON representation, the MVC app knows what object to deserialize it to. This is directly a benefit of being in charge of both producer (the API) and consumer (the MVC web project).

In the sample app available on GitHub, the project organization looks like this:

The Common project holds what are known as the DTOs (Data Transfer Objects). The DTOs do nothing but move data from one place to another; they will not ever have any methods or logic contained within them.

Once we have a place to put the DTOs, we can begin building the API which uses them.

Building the API

Let's now see how we might build both the DTO objects and the API which serializes them to JSON. First, here's a sample User class:

public class User  
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Username { get; set; }
}

For our API, let's pretend we have a ridiculously simple scenario: we need an API which returns all Users in our data store, and an MVC web application which displays these users to our end-user.

For this simple demo, let's pretend that our API Controller is returning a list of users, like so:

[RoutePrefix("users")]
public class UsersController : ApiController  
{
    [HttpGet]
    [Route("all")]
    public IHttpActionResult GetAll()
    {
        var users = new List<User>();

        users.Add(new User()
        {
            FirstName = "Marco",
            LastName = "Polo",
            DateOfBirth = new DateTime(1254, 1, 14),
            Username = "greatadventurer"
        });

        users.Add(new User()
        {
            FirstName = "Kublai",
            LastName = "Khan",
            DateOfBirth = new DateTime(1215, 9, 23),
            Username = "khanofkhans"
        });

        users.Add(new User()
        {
            FirstName = "Kokachin",
            LastName = "",
            DateOfBirth = new DateTime(1255, 8, 17),
            Username = "blueprincess"
        });

        return Ok(users);
    }
}

Now, to prove this works, we can call this action using something like Postman:

As we would expect, calling the path /users/all results in us getting back the list of users.

Now let's see how we might build an MVC web app to consume this API call.

Building the MVC API Client

The MVC app will use the brilliant NuGet package RestSharp to simplify our calls to the API. Once we've downloaded the package, we can create a class which derives from that package's RestClient like so:

public class ApiClient : RestClient  
{
    private static readonly string BaseURL = "http://localhost:55675/";

    public ApiClient() : base(BaseURL) { }

    public List<User> GetAllUsers()
    {
        RestRequest request = new RestRequest("users/all");
        RestResponse response = base.Execute(request);
        return JsonConvert.DeserializeObject<List<User>>(response.Content);
    }
}

Let's walk through each of the three lines involved in the method GetAllUsers().

The first line is

RestRequest request = new RestRequest("users/all");  

This line creates a RestRequest object which is pointed at the url http://localhost:55675/users/all (the root of the URL is initialized in the ApiClient's constructor).

The second line is

var response = base.Execute(request);  

This line calls the API at the specified location and returns an object of type RestResponse.

The third line is the most complex:

return JsonConvert.DeserializeObject<List<User>>(response.Content);  

Let's break down the key pieces of this line:

  • response.Content is the actual content from the API call; in this case, it's the JSON representation of the list of Users.
  • JsonConvert.DeserializeObject<T>() deserializes a string into an object (in our particular case, into a List<User>.
  • The List<User> is then returned to the calling method.

With this step in place, we can finish the MVC app.

Finishing the MVC App

Our MVC controller will need to create an instance of ApiClient so that it can retrieve the users from the API:

[RoutePrefix("users")]
public class UsersController : Controller  
{

    [HttpGet]
    [Route("index")]
    public ActionResult Index()
    {
        ApiClient client = new ApiClient();
        return View(client.GetAllUsers());
    }
}

Finally, all we have to do is run the MVC app and we'll see that the users are returned and displayed exactly as we thought they would be:

Summary

This organization allows both producer and consumer to use the same Data Transfer Objects (DTOs) when serializing to or deserializing from JSON objects. Consequently, both "sides" of the relationship can use the same objects without needing to write and maintain their own definitions.

I understand that this organization is only useful in situations where the developers control both sides of the producer/consumer relationship; but in those situations, it's been invaluable for me and my team.

Don't forget to check out the sample project on GitHub, and feel free to submit suggestions for improvements there or in the comments below.

Happy Coding!

"Simpler" Is Subjective: How Bad Assumptions About Architecture Kicked My Ass

I'm doing some heavy refactoring on a project that I've recently joined but that has been underway for months. The code is a mishmash of different styles and an uncertain architecture, so my task up to this point has been to make it more consistent, testable, and readable.

One of the issues I was tasked with was improving the data access layer. The system was using a pattern known as Repository for its data access layer, and the individual Repositories (the classes which both write data to and retrieve data from the database) are fairly dumb; they just do exactly what they say they do, without any validation or anything. Get, add, edit, delete, search; that's it.

The service interface layer of this app is an ASP.NET Web API application, so that app's Controllers need some way to get data from the database. In simple scenarios, the Controllers can just call the Repositories directly. However, the previous development team also included another layer of static classes between the Controllers and the Repository, called the Logic layer. The full architecture looked something like this:

Client  
|
Controllers  
|
Logic  
|
Repositories  
|
Database  

I had a problem with the Logic layer. For the majority of the functionality the app handled, the Logic layer just called a Repository method and didn't do anything else. Further, it was a set of static classes, and therefore couldn't take advantage of the dependency injection scheme that I wanted to use in the Controllers. It seemed to me like an unnecessary layer of architecture, and so I removed it; the Controllers now called the Repositories directly.

Client  
|
Controllers  
|
Repositories  
|
Database  

Long time readers will know that this is par for the course for me: I'm an extreme deletionist and any code we keep must have a reason to exist.

On the surface, the architecture change makes sense. I'd been preaching to this group that controllers need to be small, repositories should only do data access, and less architecture is better than more. Seemed like a no-brainer to me. Little did I realize the no-brainer was me.

Did you catch the mistake I'd made? I'd waltzed into this project, taken over, and declared that I knew better than the two developers who had been working this project for six months prior. I had (unintentionally, to be sure) declared that my opinion on how this app should be constructed was more important than theirs, simply because it had worked for me in the past. I had given myself a golden hammer, and now everything looked like a nail.

I, of course, didn't notice at all. The other two did. For this post we'll call them Rajit and Dave. Here's how that conversation went:

Dave: Hey Matt, did you remove the Logic classes that Rajit and I set up?

Matthew: Sure did! I couldn't see that they were doing anything different than if the controllers called the Repository classes directly. So I moved all that code to the Controllers. Now we don't need anything between Controllers and Repositories. It's a simpler design overall.

Rajit (stunned): ...Um, OK, it is simpler, kind of. But remember, this API will need to call other APIs to get all the data we need to send back to the requesting application. Where would it do that?

Matthew (realizing the mistake, not wanting to admit it): Well, wouldn't it do that in the Controllers....?

Dave (annoyed): But didn't you say that we want to keep the controllers small? Why bloat them like this when we could do the extra, non-database-access code in a different layer then just call that from the controller? Or are we not worrying about bloat on the controllers? Which is it?

Matthew: Because...well...(facepalm)

It's easy to fall into the trap of "I've seen this before and X worked, so I'll just do X again". Programmers (including myself) are busy people, often over-worked, and don't necessarily have time to consider all possible solutions to a given problem. Sometimes, the lack of time (or foresight, as is the case here) comes back to bite us in a big way.

Dave and Rajit were right, of course: we ought to have that extra layer to do things like consolidate calls from other web services and make multiple Repository submissions. I hadn't immediately found the Logic layer's reason to exist and had removed it, when in reality there was a purpose for this layer; I had just missed it. I still objected to the Logic layer being a set of static classes, so eventually Dave, Rajit, and I compromised to include a new set of Service classes:

Client  
|
Controllers  
|
Services  
|
Repositories  
|
Database  

The Services could call the Repositories and any other services, and because they were no longer static, we could inject them into the Controllers. I got my simpler architecture, and Rajit and Dave got their extra layer. Everyone's happy.

Point is, don't just assume that every app needs the same architecture. There are a myriad of software patterns for this reason: each of them has a different purpose, a different use case. They aren't all interchangeable. Don't assume the solution to a given problem is the same as a similar problem you've encountered before. Similarity does not mean sameness.

(Also, it helps if you actually talk to people who have more experience than you on a given project. Just sayin'.)

Anybody screwed up like this before? Alternately, any of you lovely readers find an over-architected app and pare it down successfully? Was there a better pattern for this app that Rajit, Dave, and I could've used? Share in the comments!

Happy Coding!