DEV Community

Cover image for Introducing Gate Classes (Design Pattern)
James Hickey
James Hickey

Posted on • Originally published at blog.jamesmichaelhickey.com

Introducing Gate Classes (Design Pattern)

Originally posted on my technical blog

Have you ever heard of guard clauses? Steve Smith discusses them in one of his Weekly Dev Tips here.

Today I want to show you a technique that I've been using which is similar to the guard clause pattern but is used in more advanced scenarios. These scenarios include when you have problems due to external dependencies or complex logic that is required in your guard clauses.

With guard clauses, we would take code that has nested conditional logic like this:

if(order != null)
{
    if(order.Items != null)
    {
        this.PlaceOrder(order);
    }
    else {
        throw new ArgumentNullException("Order is null");
    }
}
else {
    throw new ArgumentNullException("Order is null");
}
Enter fullscreen mode Exit fullscreen mode

Then we would invert the conditional logic and try to "fail fast":

if(order?.Items == null)
{
    throw new ArgumentNullException("Order is null");
}

this.PlaceOrder(order);
Enter fullscreen mode Exit fullscreen mode

Notice that right-off-the-bat we will try to make the method fail? That's what I mean by "failing fast".

Next, we might create a reusable method out of this:

public static void IsNullGuard(this object me, string message)
{
    if(me == null)
    {
        throw new ArgumentNullException(message);
    }
}
Enter fullscreen mode Exit fullscreen mode

Finally, we can use this guard clause anywhere we need:

order?.Items.IsNullGuard("Order is null");
this.PlaceOrder(order);

Enter fullscreen mode Exit fullscreen mode

This will keep our code much cleaner, avoid any nested conditions, and be way easier to reason about!

What If You Have Dependency Baggage?

I love this pattern.

But, sometimes you might find yourself trying to build a type of guard clause which has certain external dependencies, like a repository or HttpClient. Perhaps the logic for the actual guard is quite complex too.

Examples might include determining if:

  • A user has proper permissions to view a certain resource in your system
  • A potential order is capable of being purchased (in a simple retail system)
  • An insurance claim is capable of being approved
  • etc.

What I like to do in these cases is use what I've been calling "Gate Classes." They're like guard clauses, but they are classes... Go figure.

Think of these as a series of gates which each request in the system has to go through (just like middleware). If any of them fail, the gate is closed and the request cannot proceed any further.

Let me show you what I mean.

Checking If We Can Approve An Insurance Claim

Imagine we are building part of an insurance processing system.

Next, we have to check whether the claim is able to be approved, and if so, approve it.

Here's our use case (Clean Architecture) or, as some might know it, our Command (CQRS):

public class ApproveInsuranceClaimCommand : IUseCase
{
    private readonly IInsuranceClaimRepository _claimRepo;
    private readonly IUserRepository _userRepo;

    public ApproveInsuranceClaimCommand(
        IInsuranceClaimRepository claimRepo, 
        IUserRepository userRepo
    )
    {
        this._claimRepo = claimRepo;
        this._userRepo = userRepo;
    }

    public async Task Handle(Guid claimId, int approvingUserId)
    {
        User user = await this._userRepo.Find(approvingUserId);

        // 1. Figure out if the user has permission to approve this...

        InsuranceClaim claim = await this._claimRepo.Find(claimId);

        // 2. Figure out if the claim is approvable...

        claim.Approve();
        await this._claimRepo.Update(claim);
    }
}
Enter fullscreen mode Exit fullscreen mode

For the logic that will go into the comments I made, what if we required more repositories to make those checks?

Also, what if other use cases in our system needed to make those same checks?

Perhaps we have another use case called ApproveOnHoldInsuranceClaimCommand that will approve an insurance claim that was, for some reason, placed on hold until further documentation was supplied by the customer?

Or, perhaps in other use cases we need to check if users are able to have permission to change a claim?

This will lead to messy code and a lot of copy and pasting!

The Solution From The Outside

Just like the guard clause refactoring pattern, why don't we do the same thing but convert each guard clause into an entirely new class?

The benefits are that we can use dependency injection to inject any dependencies like repositories, HttpClient, etc. that only each gate class will require.

Now, our use case might look like this (keeping in mind that each gate class may do some complex logic inside):

public class ApproveInsuranceClaimCommand : IUseCase
{
    private readonly IInsuranceClaimRepository _claimRepo;
    private readonly CanUserApproveInsuranceClaimGate _canUserApprove;
    private readonly CanInsuranceClaimBeApprovedGate _claimCanBeApproved;

    public ApproveInsuranceClaimCommand(
        IInsuranceClaimRepository claimRepo
        CanUserApproveInsuranceClaimGate canUserApprove, 
        CanInsuranceClaimBeApprovedGate claimCanBeApproved
    )
    {
        this._claimRepo = claimRepo;
        this._canUserApprove = canUserApprove;
        this._claimCanBeApproved = claimCanBeApproved;
    }

    public async Task Handle(Guid claimId, int approvingUserId)
    {
        await this._canUserApprove.Invoke(approvingUserId);
        InsuranceClaim claim = await this._claimRepo.Find(claimId);
        await this._claimCanBeApproved.Invoke(claim);
        claim.Approve();
        await this._claimRepo.Update(claim);
    }
}
Enter fullscreen mode Exit fullscreen mode

Notice that there's no more need for the IUserRepository since it will be handled by the CanUserApproveInsuranceClaimGate gate class (and DI).

Note: Why didn't I make an interface for each gate class? Just for simplicity. But yes, by using interfaces instead of a concrete class you may mock them much easier for testing.

Creating A Gate Class

Let's look at how we might build the CanInsuranceClaimBeApprovedGate gate class:

public class CanInsuranceClaimBeApprovedGate
{
    private readonly IInsuranceClaimAdjusterRepository _adjusterRepo;
    private readonly IInsuranceClaimLegalOfficeRepository _legalRepo;

    public CanInsuranceClaimBeApprovedGate(
        IInsuranceClaimAdjusterRepository adjusterRepo,
        IInsuranceClaimLegalOfficeRepository legalRepo
    )
    {
        this._adjusterRepo = adjusterRepo;
        this._legalRepo = legalRepo;
    }

    public async Task Invoke(InsuranceClaim claim)
    {
        // Do some crazy logic with the data returned from each repository!

        // On failure, throw a general gate type exception that can be handled 
        // by middleware or a global error handler somewhere at the top of your stack.
        throw new GateFailureException("Insurance claim cannot be approved.")
    }
}
Enter fullscreen mode Exit fullscreen mode

Each gate class will either succeed or fail.

On failure, it will throw an exception that will be caught up the stack. In web applications, there is usually some global exception handler or middleware that can convert these into specific HTTP error responses, etc.

If we do need to use this logic in other places, as mentioned above, then we don't need to re-import all the dependencies required for this logic. We can just simply use the gate class as-is and allow the DI mechanism to plug in all the dependencies for us.

Some Caveats

It's worth mentioning, that in some cases your use cases and your gate classes may need to call the same repository method. You don't want to be fetching that data twice (once in your gate class and once in your use case).

In this event, there are ways to fix it.

One is to build a cached repository using the Decorator pattern.

You might rig this up as a scoped dependency (in .NET Core) so the cached data will only be cached for the lifetime of the HTTP request. Or you might just set a timeout on the cache.

Another way is to allow the use case to inject the raw data into the gate class as a dependency.

In any event, this pattern is very helpful in making your code much easier to test, use and maintain!

What Are Your Thoughts?

Have you seen this technique before? Have any thoughts? I would love to hear them!

Keep In Touch

Don't forget to connect with me on:

You can also find me at my web site www.jamesmichaelhickey.com.

Navigating Your Software Development Career Newsletter

An e-mail newsletter that will help you level-up in your career as a software developer! Ever wonder:

✔ What are the general stages of a software developer?
✔ How do I know which stage I'm at? How do I get to the next stage?
✔ What is a tech leader and how do I become one?
✔ Is there someone willing to walk with me and answer my questions?

Sound interesting? Join the community!

Top comments (10)

Collapse
 
raimeyuu profile image
Damian Płaza

Thanks for sharing this pattern, looks really interesting. However, I feel worried about Invoke method signature of such Gate class. It communicates it's async and does not return anything (by returning Task) while in fact it implicitly throws exceptions here and there. I know that having a global exception handler is common practice, but I don't feel it's the best what we can do.

In fact, such Gate approach is quite common in functional programming, but it's encoded in method's signature by using Result type. Here's a good explanation with showcase of such approach: enterprisecraftsmanship.com/2015/0...

I am curious about your opinion :-)

Collapse
 
jamesmh profile image
James Hickey

Throwing exceptions in the non-application layer is a convention that some people just plain detest, and some find is just more pragmatic.

I wrote about this a bit in this article:

Specifically, in one section, I mention that one possible alternative is the Result Object pattern, which is basically the pattern used in the link you provided.

So I agree, sometimes it might make more sense to use some custom class or "error" data structure to pass back and forth.

This article is great on the subject. It looks at 3 different ways to do this actually. While my conclusions are the exact same as his, he gives more details for why, whereas that wasn't an issue I specifically was tackling in my article, but just mention it in passing.

It's common in DDD due to the fact of using Value Objects and the "Always Valid" pattern, essentially.

Let me know what you think! 👍

Collapse
 
stemmlerjs profile image
Khalil Stemmler

This is mighty clean, I appreciate how SOLID this is. Good distinction between the Use Case and Command from CQRS as well, that just clicked now.

As for this way of structuring policies, I like it! I think I'll use it myself.

Thanks for this one, James.
Keep it up

Collapse
 
jamesmh profile image
James Hickey

Great! I've been using it with lots of success. Def. makes re-using policies etc. super easy to re-use and test 👍

Collapse
 
lalitkale profile image
Lalit Kale

This is quite similar to what Guard class in many open source projects has been used for. Moreover, fluentvalidations lib provides many other features. You or readers might be interested to take a look.

Collapse
 
jamesmh profile image
James Hickey

Yes. If people are getting into a CQRS or use case style architecture, then using Mediatr is a fantastic tool.

You can register pre-processor handlers that run before each handler. These gate classes I'm discussing can easily be converted as a pre-processor handler (that's essentially the same pattern).

Collapse
 
glenmccallumcan profile image
Glen McCallum

First time I've seen this! Can't wait to try it out.

Collapse
 
jamesmh profile image
James Hickey

Let me know when you do 👍 It's been working well for me!

Collapse
 
glenmccallumcan profile image
Glen McCallum

Where do you put your gate classes in your project package/folder structure?

Thread Thread
 
jamesmh profile image
James Hickey

Usually, I have a project structure like:

web (app layer) -> use cases -> domain project

I tend to put all these kinds of classes in the use cases project. I'll have a structure using feature folders something like:

  • Features
    • Orders
    • Payments
    • Shipping

For cross-cutting stuff like checking a user's role, etc. I would create another folder in the root of the use case projects called "Gates".

If a specific gate class is really tied to a specific feature of the application, which is rare since they are mostly useful for cross-cutting kinds of concerns like checking user permissions etc., then I would just make a "Gates" folder in the features folder:

  • Orders
    • Gates (folder)
    • PlaceStandardOrderCommand.cs (use case)
    • GetActiveOrderForUserQuery.cs (use case)
    • ...and more use cases...