Building PuluAuth: A Journey in Exploring Hexagonal Architecture - Part 1

The Beginnings

·

8 min read

I have been a web developer for quite some time. I started learning web development during my junior high school days and am still doing it today, some twenty years later. I have tried many languages and frameworks, from ASP to TypeScript, from CodeIgniter to Next.js, building (or at least contributing) small personal home pages to high-traffic applications, and a lot of things in between.

However, even though my tech stack changes now and then, I still mostly build websites while using the good old MVC design patterns. Whenever I try to learn new things to implement in a new project, I keep getting back to using MVC again and again.

One reason is the deadlines. I (aren’t we all) have limited time to finish my projects while also finishing all those video games in between! However, time constraints aside, I also never see the need to go beyond the trusty MVC. I always thought that everything else just adds unnecessary complexity and overheads.

“Why should I add these gazillions of extra classes and interfaces when all you need is just (mostly) two classes?”

This is the question that I keep asking myself and others. The answers, however, are mostly the same:

“Separation of concerns, maintainability, testability, scalability, reusability, and improved readability of code, blah blah blah”

My immediate response is usually:

“Maintainability, readability, blah blah blah… yeah haha nice try, now try reading your gazillion classes and interfaces that just basically do a select query that I can do in an easy three-step process”

But, now is different, now is the time for me to be more open-minded, now is the time for a change™. Or maybe I’m just bored with doing the same stuff repeatedly. So now I’m going to do it. I want to learn about a new architectural pattern that’s popular now!

But, what?

Let’s start with the biggest challenge: what to learn? Long story short, I decided to learn about Hexagonal architecture. Why? Just because! But really, I’m not sure why either, I just keep hearing it time and time, so it must be popular and it looks kinda interesting.

As the adage goes, the best way to learn something is by building a blog with it! Want to learn PHP? Build a blog! Want to learn Typescript? Build a blog! Want to learn AwesomeJsFramework#999384-debuno-AI-999b-asdf? Build a blog! Want to learn how to play guitar? Build a blog!

So building a blog it is.

“Hmm, wait, why not make it even more interesting and different than what I usually build (Ed: a monolith btw, remember, I am old-school), why don’t I build this blog as a REST API backend with OAuth 2.0 and then build a separate frontend app with the latest Buzzwordscript framework! How’s that for separation of concerns!”

Good job brain! Why choose the easy way when there’s always a hard way, amirite?

“OK, then I will need some OAuth 2.0 provider, a cloud one? Nah, I want something that I can run locally, …yeah right not that either, that looks too complex for a simple blog! Oh, I know! Why don’t I just build my own instead!”

Again, good job brain! Best brain I ever had! So instead of learning Hexagonal architecture by building a blog, now I’m going to learn Hexagonal by building an OAuth 2.0 server! Awesome!

“While on it, why not document the whole process in a blog too? It might be helpful for someone, or, at least, for myself!”

Great… But you know? For every blogging application I’ve ever built. Never have I written a single blog post for public consumption! Well maybe I did… but it was so cringey back then. Not that this won’t be cringe either. For whoever reading this, please accept my sincere apology in advance.

Ahem… now please pardon me while I reflect on my life choices for a bit.

The requirements

As with every proper SDLC, let’s set some basic requirements and guidelines for the projects:

  • An OAuth 2.0 authentication server, supporting only:

    • Authorization Code Grant with PKCE extension, for authenticating users using their credentials, i.e. username and password,

    • Client Credentials Grant, for application-to-application communications, and

    • Refresh Token Grant, so expired tokens can be renewed without needing to re-authenticate.

  • Build in PHP 8.3 (or 8.4 when it’s out, at the time of writing it’s still in RC3), using league/oauth2-server as the main library.

  • Using MySQL as the main database, and Valkey for caching the tokens.

  • Leverage PHP’s PSRs whenever applicable.

  • Administration REST API, using simple API key and signature-based authentication.

  • And maybe others!

    • (Hey! What projects don't have requirements change in the middle of development? This is a very important, realistic SDLC phase!)

      • (at least this time, I AM the CEO, CTO, and all the other C-level!)

The architecture

Disclaimer first: If it’s not obvious by now, all of these are based on my understanding, while I may be correct, I may also be wrong (or very wrong). And yes, I haven’t read the book!

So anyway, the Hexagonal architecture (also called ports and adapters), is an architecture that aims at enforcing separation of concerns not only between parts of the application but also with the outside world. It is also said that this architecture is the origin of the microservices architecture. As you can see from the name, the main features of this architecture are the “ports” and “adapters”.

But, before we go into the ports and adapters, we should take a look at the “core” of the application first. Like every other application out there, you build an application because you have a problem to solve (or maybe you just asking for it, like me), and that problem is the core of every application. The cool people call this “business logic” which should be implemented in the “domain” layer.

For PuluAuth, we have two main problems that we want to solve:

  • We want other applications to be able to use PuluAuth as their OAuth 2.0 authentication service, as long as those applications only use the supported grant types, and

  • We want the PuluAuth administrator (that probably would be just me (or you!)) to be able to manage it via HTTP REST API.

Looks pretty simple so far. Next, for PuluAuth to be able to solve those problems, it would need to interact with the outside world. The interactions required are:

  • Other applications need to be able to connect to PuluAuth as specified by the OAuth 2.0 specification to do their authentication stuff,

  • PuluAuth’s administrators need to be able to connect to PuluAuth by using HTTP REST API to manage it,

  • PuluAuth needs to be able to connect to the MySQL database to store and retrieve data, and

  • PuluAuth needs to be able to connect to the Valkey cache to store and retrieve data.

For those interactions, we will create “ports” for our application core so it can do those interactions. So, we will create the following ports:

  • The OAuth port, for the OAuth functions,

  • The Administration port, for the server administration functions,

  • The Repository port, for storing and retrieving data from the database, and

  • The Cache port, for storing and retrieving cache.

These ports will be a specification (an interface) for how PuluAuth’s core interacts with the outside world. Those specifications will then be implemented by the “adapters” (hence “ports and adapters”). For that reason, other than the OAuth port, we will make the other ports as generic as possible so that we can modify or even switch the implementation without affecting the core business logic.

By the way, that paragraph just above is one of the reasons why up until then I never really bothered to learn about other architecture, like, just be honest, how often do you actually switch those implementations? Most likely never, or at least very rarely. In all of my experience, I only have ever done it once. And that was switching from MySQL to PostgreSQL, which then again, I basically did nothing as it was all handled automatically by PDO. And it was just two months ago. And the reason is just because the client thinks that PostgreSQL is… more secure than MySQL.

So people actually DO switch implementation! Wow, what a shocking fact! I am so shocked. So shocked to the point I want to learn another architecture that enables that kind of thing!

Anyway, for now, we only need one implementation each for those ports. But know that we will be able to switch the implementation if needed in the future. Maybe the MySQL licensing will require you to sacrifice your children (on one child per one CPU core basis), or maybe Valkey will be gone in three months. Who knows?

The other advantage of this approach is testability. If we (and that’s a gigantic IF) built the test properly, when we want to modify or even switch the implementation, we can ensure that our application still works by running the test.

Again, that is also one of the reasons I’m too lazy to learn other architecture. There can never be a perfect test, so why bother? But, we still can try our best. And by blogging this process, I hope I can try my best.

Back to our adapters, so will build the following adapters:

  • The HttpOAuth adapter, for implementing the OAuth functions with HTTP (as that’s what they are now, as far as I know),

  • The HttpAdministration adapter, for implementing the administration functionalities with HTTP (maybe we’ll implement NeuralInterfaceAdministration next year)

  • The PdoRepository adapter (maybe we’ll need PunchCardRepository later for some exotic installation), and

  • The ValkeyCache (in case Valkey is gone, we’ll build HumanBrainCache later).

Looks pretty good right? Now, let’s see some tl;dr diagram:

What did you say? That’s a square? Not a hexagon? Nah, I’m too lazy to make that.

In the next part, we will actually do some coding and try to implement those!