This article includes diagrams that may cause issues with screen readers, and are unlikely to be legible on small screens.

A couple of months ago I was writing some educational material about multitenanted web applications, and I decided to try and define a handful of architectural patterns for multitenancy. During this, I was writing about tenant identification, and how the process of identifying a tenant for any given request, is virtually identical to the process of identifying a user.

This lead me to consider that the underlying process of these two things, is actually a pattern in its own right, and it can be applied to many other similar situations. After much research, I discovered that while there are similar patterns, there are none that define this specific, well used process. So I decided to try and define it myself, and this article is the result of that.

Defining a Pattern

Before I fully introduce you to the Request-derived Context pattern, I wanted to talk a little about defining a pattern. Both design and architectural patterns will typically already exist, before the individual defining them does so. The majority of the patterns from the Gang of Four, or Martin Fowler, were already in use, they just didn't have a name, so lacked any sort of formal vocabulary or terminology.

I want to make it abundantly clear that I am not claiming to have invented the concept this pattern defines. I am simply trying to give it a name, and a shape, so that it can be discussed, and understood by others.

Request-derived Context

This Request-derived Context pattern is an architectural pattern, so it models a process and the general structure of a system within an application. The process it models is that of retrieving the relevant context for a given HTTP request. The context itself is derived from the request, hence the name.

The idea is that an incoming request contains a context source, which is used to derive the context. That source is extracted by a context extractor, and then consumed by a context resolver, which retrieves the context. The context is then persisted for the duration of the request in a context store.

If you were to visualise the sequence/flow of this process, it would look something like this.

ContextStoreContextResolverContextExtractorRequestHandlerClientContextStoreContextResolverContextExtractorRequestHandlerClientIncoming HTTP Requestextract(request)ContextSourceresolve(ContextSource)Contextstore(Context)Proceed with contextual request

The above description and diagram may not be all that clear, however, I can assure you that almost every one of you will be familiar with this process, at least in part. Even though, to the best of my knowledge, I'm the first one to attempt to define this pattern, it is something that has been used for decades. There are, in fact, two very common usages of this pattern, server-side sessions, and user authentication.

Server-side Sessions

The chances are, that if you're reading this, you're a Laravel developer, or at the very least, a PHP developer. If so, you're most likely familiar with the concept of server-side sessions, typically just called sessions. Some of you may even be familiar with how they work under the hood, but for those who aren't, let me explain.

When a user visits a website, such as an ecommerce store, a session will be created for them. This session is used to store the state for that visitor which allows it to persist across requests. However, since HTTP is a stateless protocol, the server has no way of knowing who the user is on subsequent requests. To solve this, the server assigns each session a unique identifier, referred to as a session ID. This session ID is provided to the client in the response of the first request, and is then sent back to the server on every subsequent request.

SessionStore Server Browser SessionStore Server Browser HTTP Request (no session cookie) Generate new session ID + store empty session Session created Set-Cookie: session_id=abc123 HTTP Request with Cookie: session_id=abc123 Load session with ID abc123 Return session data HTTP Response (session updated if needed) Save session data (if modified)

The cookie itself would typically be encrypted, but I've left that out for clarity.

Within Laravel, the context source is the laravel_session cookie, which is extracted by the StartSession middleware (the context extractor), and then both resolved and stored by the the SessionManager class (the context resolver and context store).

User Authentication

Another common example of this pattern is user authentication. When a request is received by the server, something within the request will identify the current user, allowing the server to know who is making the request. There are lots of different methods of authentication, but the most common uses sessions, and actually builds upon the process above.

When dealing with sessions, the session ID that comes from the session cookie, can be used directly to resolve the sessions. This means that the session_id cookie is known as a direct context source. However, when dealing with user authentication via session, the session ID becomes a users indirect context source. To resolve the user, you must first resolve the session, and then locate the user ID stored within it.

We can visualise this by adding to the session diagram from above.

UserProviderSessionStoreServerClientUserProviderSessionStoreServerClientHTTP Request (no session cookie)Generate new session ID + store empty sessionSession createdSet-Cookie: session_id=abc123HTTP Request with Cookie: session_id=abc123Load session with ID abc123Return session dataLoad user by session.user_idReturn UserHTTP Response (user-aware)Save session data (if modified)

As mentioned above, the context source is still the session_id cookie, but in this situation the context extractor has the additional step of resolving the session, and then extracting the user ID (the direct context source). Then the user which is the context, is resolved using a UserProvider, which serves as our context resolver.

In a modern web application, dependency injection would be used, so the context extractor would actually depend on the session, so it wouldn't need to resolve it itself. Both the user context, and the session context, would also likely be lazy loaded, so they would only be resolved when they are actually needed.

Within Laravel, users are resolved only when they are needed, and the context extractor is the relevant Guard class that's configured for the relevant auth type. The guard contains an instance of the UserProvider class, which functions as the context resolver. It also keeps track of the user context, making it also the context store. Laravel does also offer remember me functionality, which provides a second context source for the user, should the session have expired.

There are obviously other methods for user authentication, such as API or JWT tokens in the Authorization header, dedicated cookies, or even custom headers. Whatever the method employed, the process is still the same, as the context source is contained within the request.

And So On and So Forth

Server-side sessions and user authentication are just two examples of the pattern, and I used them as they're ones that most of you will be familiar with. However, the pattern can be applied to anything. Have a web application that has multiple sites on different domains or subdomains? The domain, or the subdomain becomes the context source, whether it's multitenanted or not. Does your web application have a hierarchical URL structure, so when accessing a URL prefixed with /project/my-project, everything after is scoped/related to the project my-project? Well, that portion of the URL is the context source.

This is a versatile pattern that can be applied to many situations, and is actively being used. It's honestly surprising that it doesn't have a name, or any sort of formal definition. Hopefully this article will help you understand the thought process that lead me to try and define one.

The Components of The Pattern

In the examples above I mentioned the individual components of the pattern, and even included a diagram that shows you the flow of this process. Hopefully, the names of each component, as well as with the added context (no pun intended) of the examples, have made it pretty clear what they each do. However, I'd like to expand on this with a more formal description and definition of each component, if only for clarity sake.

Context

The context isn't really a component, and it's just about a role, but it's important to understand what it is. The context is the information that is derived from the request, and can be anything, whether it's a session, user, tenant, locale, project or quite literally anything else. It doesn't have to be a class/object, it can be a simple value, but will often be one.

The context will define the bounds that the handler of a request will operate within, which can affect the data is available, the actions that can be performed, and the overall behaviour of the application. Because of this, requests should only ever have one active context for a given context type. If a request should operate within the context of multiple users for example, then that's not the user context, that's some sort of hierarchy or relationship context, and should be treated as such.

Context Source

The context source is any value, or piece of data that exists within a request, that can be used to derive the context for that request. This means that the context source can be included in pretty much every part of the request, such as.

There are two types of context source, the first is known as direct, and refers to values that can be used directly to resolve the context. The second is known as indirect, and refers to a value that would typically require some sort of lookup or resolution to retrieve a direct context source. I'll admit, that the line between the two is a little blurry, as it really depends on what exactly is being resolved.

For example, a session ID cookie that's encrypted, would be considered a direct context source for the session, even though it requires decryption. However, a JWT token in the Authorization header, would most likely be considered an indirect context source, because it has to be decoded, parsed, and then the user ID extracted from it. The distinction between the two is not particularly important, and only really matters when writing or talking about an application that has both.

To make things a little more complex, a direct context source would only ever be the source for one particular type of context. Whereas, an indirect context source, could be the source for multiple types of context. A direct could also be an indirect context for other types. In the examples above, the session_id cookie was the source for both the session, directly, and the user, indirectly. That same cookie could also be the indirect context source for the Cart, the Wishlist, or any other context that could be derived from the session.

Again, don't get too hung up on the detail of what exactly is a context source, and its different types. It’s simply a term used to refer to values that serve this specific role.

Context Extractor

The context extractor is a class or function that is responsible for extracting the context source from the request. Its purpose is to find the direct context source for a given context, even if that includes a lookup or some other form of processing. They do not need to be distinct from other components, they are more of a role than a separate class or function. Each type of context should have its own context extractor, whether that's a totally separate class or function, or a generic approach with arguments to specify the context type.

As mentioned in the examples earlier on in this article, within Laravel, the StartSession middleware is the context extractor for the session context, and the Guard class is the context extractor for the authentication context. However, the StartSession middleware extracts the session ID, and tells the session manager what it is, but doesn't actually start any resolution or lookups, so functions as only a context extractor. The Guard class, on the other hand, extracts the user ID from the session, resolves the user, and then stores it, functioning as a context extractor, context resolver and context store.

Context Resolver

The context resolver is a class or function that is responsible for resolving the context from a direct context source, extracted from a request by a context extractor. Much like the above, the context resolver is more of a role, and as such does not need to be distinct from the other components. It is quite common for a single class to be either a context extractor, and context resolver, or a context resolver and a context store. Context resolvers are also per context type, whether generic or specific.

Context Store

The context store is a class responsible for keeping track of the current context for the duration of the request. A context store can be per context type, but when dealing with context that can be mapped to classes, a generic context store could be used.

Again, the context store is more of a role than a distinct class, so its implementation and combination with other components is entirely down to the developer. It's not required, but it is recommended that your context store is capable of distributing the context and its source to the relevant places. This can include things like globally setting the default value for a route parameter, or adding a cookie or header to the response.

A Real Life Example

I've covered a couple of different implementations of this pattern within Laravel, but I'd also like to point you towards a more apparent example of it. I'm referring to my multitenancy package for Laravel, Sprout.

The context extractors in Sprout are implementations of the IdentityResolver interface. Out of the box it supports five different types of context source, subdomains, headers, the URL path, cookies and sessions. The first four are direct context sources, and the final one, the session approach, is indirect. These are all generic context extractors that require values to configure them, such as the header, cookie or route parameter name.

Once the context source has been extracted, it is passed to a context resolver , which is an implementation of the TenantProvider interface. The package comes with two generic implementations, one for Eloquent, and one just using the database.

Finally, the context store is an implementation of the Tenancy interface. In sprouts implementation, this class also contains the context resolver, and sort of functions as a bit of a proxy context resolver.

In Sprout, the context is a tenant, but you would refer to it as whatever the implementation is. This is because it allows for multiple tenants to be active at the same time, though only one of each type, to allow for child tenants, or other hierarchical relationships.

An Example Implementation

I've covered everything that I wanted to cover, but before I finish and leave you to ponder on this, I'd like to show you a simple implementation of the pattern. I'd go as far as to call this example a pure, modern implementation, as it leans more into the separation of concerns, and dependency injection, than most implementations you'll find in the wild.

This whole implementation starts with a Request class, which is a pure immutable representation of a HTTP request. It contains everything that comes with the request, like the cookies and headers, but it does not know anything about the route, sessions or a user. I'm not going to give example code for this class, as it would be huge.

This example code makes use of static analysis generics. If you're not familiar with them, you can find out more here.

The Contracts

For this implementation to function there needs to be two contracts (interfaces), that define the structure of a context extractor, and a context resolver.

/**
 * @template TSource of mixed
 */
 interface ContextExtractor
{
    /**
     * @param Request $request
     * @return TSource|null
     */
    public function extract(Request $request): mixed
}

First up is the context extractor, which defines a single method, extract, that takes a Request, and returns a mixed value. I'm also creating the generic template TSource, which defines the data type the context source will be.

/**
 * @template TSource of mixed
 * @template TContext of object
 */
interface ContextResolver
{
    /**
     * @param TSource $source
     * @return TContext|null
     */
    public function resolve(mixed $source): ?object
}

Next is the context resolver, which again defines a single method, resolve, which takes a mixed argument, and returns an object or null. Here I define two custom types, TSource again, which defines the type that the context source will take, and TContext which defines the type of the context that will be resolved.

The Context Manager

In this implementation I'm adding a context manager that functions not only as a context store for all types of context, but also as a manager to facilitate the handling of context.

final class ContextManager
{
    private Request $request;

    /**
     * @var array<class-string, ContextExtractor>
     */
    private array $extractors = [];

    /**
     * @var array<class-string, ContextResolver>
     */
    private array $resolvers = [];

    /**
     * @var array<class-string, object|null>
     */
    private array $context = [];

    /**
     * @template TContext of object
     * @template TSource of mixed
     *
     * @param class-string<TContext> $type
     * @param ContextExtractor<TSource> $extractor
     * @param ContextResolver<TContext> $resolver
     */
    public function register(string $type, ContextExtractor $extractor, ?ContextResolver $resolver = null): self
    {
        if ($resolver === null && ! $extractor instanceof ContextResolver) {
            throw new \InvalidArgumentException(
                'If no resolver is provided, the extractor must implement ContextResolver.'
            );
        }

        $this->extractors[$type] = $extractor;
        $this->resolvers[$type]  = $resolver ?? $extractor;

        return $this;
    }

    public function setRequest(Request $request): self
    {
        $this->request = $request;

        return $this;
    }

    /**
    * @template T of object
    *
    * @param class-string<T> $type
    *
    * @return T|null
    */
    public function context(string $type): ?object
    {
        if (array_key_exists($type, $this->context)) {
            return $this->context[$type];
        }

        if (isset($this->extractors[$type])) {
            $extractor = $this->extractors[$type];
            $source    = $extractor->extract($this->request);

            if ($source === null) {
                $this->context[$type] = null;
            } else {
                $resolver             = $this->resolvers[$type];
                $this->context[$type] = $resolver->resolve($source);
            }
        } else {
            throw new \InvalidArgumentException(sprintf(
                'No context registered for type \"%s\"',
                $type
            ));
        }

        return $this->context[$type];
    }
}

This is quite a big class, so let's look through each bit individually.

/**
 * @var array<class-string, ContextExtractor>
 */
private array $extractors = [];

/**
 * @var array<class-string, ContextResolver>
 */
private array $resolvers = [];

/**
 * @var array<class-string, object|null>
 */
private array $context = [];

The extractors property is an array that contains a mapping of the context type to an instance of ContextExtractor. Likewise, the resolvers property is the same, except it maps context type to instances of ContextResolver. The context property is actually the backing for the context store, storing a map of context type to its current context, which could possibly be null.

/**
 * @template TContext of object
 * @template TSource of mixed
 *
 * @param class-string<TContext> $type
 * @param ContextExtractor<TSource> $extractor
 * @param ContextResolver<TContext> $resolver
 */
public function register(string $type, ContextExtractor $extractor, ?ContextResolver $resolver = null): self
{
    if ($resolver === null && ! $extractor instanceof ContextResolver) {
        throw new \InvalidArgumentException(
            'If no resolver is provided, the extractor must implement ContextResolver.'
        );
    }

    $this->extractors[$type] = $extractor;
    $this->resolvers[$type]  = $resolver ?? $extractor;

    return $this;
}

Next up is the register method, which allows for the registering of a context with its context extractor, and context resolver. Since extractors and resolvers don't have to be distinct, this method allows you to provide a single object that implements both ContextExtractor and ContextResolver. It also uses the generic types TSource and TContext.

private Request $request;

public function setRequest(Request $request): self
{
    $this->request = $request;

    return $this;
}

There's also a setRequest setter method, which populates the request property. The idea is that once the Request object is created, the ContextManager is given an instance of it, so that it can facilitate, or rather, manage, context.

/**
* @template TContext of object
*
* @param class-string<TContext> $type
*
* @return TContext|null
*/
public function context(string $type): ?object
{
    if (array_key_exists($type, $this->context)) {
        return $this->context[$type];
    }

    if (isset($this->extractors[$type])) {
        $extractor = $this->extractors[$type];
        $source    = $extractor->extract($this->request);

        if ($source === null) {
            $this->context[$type] = null;
        } else {
            $resolver             = $this->resolvers[$type];
            $this->context[$type] = $resolver->resolve($source);
        }
    } else {
        throw new \InvalidArgumentException(sprintf(
            'No context registered for type \"%s\"',
            $type
        ));
    }

    return $this->context[$type];
}

Finally, we have the context method, which returns the context for the given type, or null. This method is a little more complex than the others, so lets break it down further.

if (array_key_exists($type, $this->context)) {
    return $this->context[$type];
}

If there's already an entry in the context property, return that value. Pay special attention to the fact that this is using the array_key_exists function, and not the isset construct. This is because if the context was requested previously, and not found, the current context for that type is null, and isset would return false here, even though there is an entry in the array.

if (isset($this->extractors[$type])) {
    $extractor = $this->extractors[$type];
    $source    = $extractor->extract($this->request);

    if ($source === null) {
        $this->context[$type] = null;
    } else {
        $resolver             = $this->resolvers[$type];
        $this->context[$type] = $resolver->resolve($source);
    }
} else {
    throw new \InvalidArgumentException(sprintf(
        'No context registered for type \"%s\"',
        $type
    ));
}

If we hit this point, the context isn't already stored, so we need to attempt to find it. First we check that the context type is registered, which can be done with an isset on the extractors property. If it is not registered, we throw an exception, otherwise we continue by passing the Request object to the context extractor, to retrieve the context source.

If there's no context source returned, we can assume that this is no context of this type, and set the context to null in the context store. If there was one however, we will need to resolve it using the context resolver, adding its return value to the context store, regardless of whether it was null or not.

I'm aware that if you're using static analysis on this, it's going to complain that there's no check to see whether there is a context resolver in the resolvers property. It is safe to assume that it is there if there is an extractor, but you could throw in a check here if you wanted to be extra safe.

return $this->context[$type];

Once all the above is done, we return the context from the "context store", regardless of whether it was resolved. It's safe to assume that the context is present in the array, as the above would have ensured an entry was present.

Consuming the Implementation

Let's assume that we have the following classes in our codebase.

I'm going to assume that there's a dependency injection solution in place, because honestly, if you aren't using one, this particular implementation is a bit much. This solution, whatever it is, would need to be configured to know the following.

You could even use attributes or contextual injection methods, dependant on the solution being employed to handle dependency injection.

The Purity of the Implementation

At the start of this section I referred to this implementation as being modern, but also pure, which I know is quite the claim, but let me explain what I mean.

This implementation of the pattern provides a clean separation of concerns, not just between the components of the pattern itself, but between the components of the application. The Request class is immutable, and represents an HTTP request, which means it can be used for both incoming, and outgoing requests.

On top of this, the concept of a route, session, user or tenant has been abstracted away without introducing a whole host of inherent complexity. The HTTP layer in the application logic doesn't need to care about how those concepts are used, and nor does the controller and the rest of the business logic. Those concepts have been abstracted down into context, presented by a class that can be depended on. Code that needs the route can depend on the Route class, without depending on code that's responsible for resolving it. The same goes for the session, user and tenant.

Even if you weren't using dependency injection, or were using a much simpler solution, your code could work directly with the ContextManager class. This would be a less than ideal solution, and would very much start to encroach into the realm of the service locator pattern, but that's not that bad for this particular use case.

Final Words

As I sit here in the 26°C heat of the British summer, with the awful humidity that comes with it, I realise that I've been sat writing this article for a long time, and it's much longer than I had originally intended. So, I'll attempt to wrap it up now.

I hope that you've found this article useful, and that you understand not only the pattern I'm defining, but the benefit of having a definition for it. I think this pattern can be useful to help us approach things in a slightly different way. For example, a lot of web application frameworks implement this pattern multiple times, one for each context type, and I honestly believe that's because we treat those as completely distinct, without any connections what so ever. As you can see from my example above, that doesn't have to be the case.

To the best of my knowledge, and I've researched it a lot, there is no formal definition or pattern that matches what I have attempted to cover and define here. I have tried to give it a name that makes sense, and that allows is to reference it in the future (I for one will be adding it to the Sprout documentation).

Creating this pattern is something that caused a bit of a crisis of confidence, and I've been sitting on it for a while. That being said, I'm really happy with how it turned out, and I would absolutely love to hear any thoughts or feedback, whether that's positive or negative.

If you'd like to stay updated with my latest articles, tutorials, projects, and thoughts on everything Laravel and PHP, you can subscribe to my newsletter.

Got thoughts, questions, or just want to chat? I'm always up for a good conversation— get in touch, or find me on BlueSky, X or Discord