Leveraging Hexagonal Architecture When Building Applications

Hexagonal architecture, also known as ports and adapters, is a software design pattern used to isolate core business logic from external technologies such as databases and APIs. This separation ensures that the application core remains independent of the infrastructure, allowing developers to update or replace external tools without modifying the underlying business rules. The architecture is structured into three primary layers: the domain, which contains business models; ports, which are interfaces defining the required contracts; and adapters, which provide the concrete implementations for those contracts.

In Python, ports are typically implemented using abstract base classes to enforce dependency inversion. This ensures that high-level modules depend on abstractions rather than low-level implementation details. For example, an application might define an InstallationRepository port with a create function. A SQL adapter implementing this port would contain SQLAlchemy code, while a NoSQL adapter would use PyMongo. Because the application core interacts only with the port, switching from SQL to NoSQL requires creating a new adapter without altering the business logic.

The approach improves maintainability and testability by allowing business logic and adapters to be tested in isolation. It also facilitates parallel development, as different team members can implement separate adapters once the ports are defined. While the pattern introduces more boilerplate code and requires more upfront design time, it enhances the effectiveness of AI-assisted programming. Large Language Models can more efficiently generate standard adapter code when the intricate domain logic is clearly decoupled from the mundane infrastructure tasks.

This description was generated by Open-Source AI using the transcript of the session and the original submission contents.

This session took place in track Programming & Software Engineering & Testing and was classified suitable for novice domain / intermediate python by the speaker.

Submission

The proposal as submitted by the speaker before the conference.

This talk will cover the following related to hexagonal architecture:

Introduction The hexagonal architecture design pattern, also known as “Ports and Adapters”, was introduced by Alistair Cockburn in the early 2000s. With the increase in usage of LLMs as software development tools, this design pattern can help create clear boundaries within applications and make code more understandable and modifiable by AI tools.

Core principles and concepts In this section, I will discuss the fundamental concepts that make hexagonal architecture effective. This includes, the central application core (business logic/domain), ports (interfaces that define contracts), and adapters (implementations that handle external interactions, for example interaction with a database or external services).

Benefits and problem-solving capabilities The discussion will highlight benefits including enhanced testability, improved maintainability by reducing coupling, and easier technology migration. I'll demonstrate how hexagonal architecture addresses common development pain points such as database lock-in, framework dependencies, and the challenge of writing effective unit tests.

Implementation and real-world case study Included in this presentation will be a real-world case study of how hexagonal architecture is implemented in a production application. This example will demonstrate how to handle common scenarios such as database functionality, external API integration, and user management. The case study will show actual Python code, highlighting patterns for repository implementations, service layers, and adapter configurations.

Conclusion and Q&A The presentation concludes with the key takeaways, resources for further learning, and an interactive Q&A session.

Transcript (auto)

Auto-generated from the recording utilizing Open-Source AI. Speaker labels (Speaker 1, Speaker 2) reflect diarization, not identity. Timestamps refer to the recording.

Speaker 1 [00:31]

Hello, everyone. Welcome to Platinum. And we are soon going to start a new talk from Luke. Before that, just the usual announcement about lightning talks. If you haven't already, please consider it. And now I'll hand it over to Luke for his talk about software architecture, software development. Please give him a huge round of applause.

Speaker 2 [01:06]

Thank you everyone. Thank you for joining the talk today. I will be talking about leveraging hexagonal architecture when building applications. And just some background on me, I'm a software engineer at Rosen Next, working in the US, but fortunate to be able to join you all here for the conference. Just to go over the agenda for today, I'll start with an introduction to hexagonal architecture, then talk about the core principles and concepts, and then go into the benefits and problem solving capabilities. Then I want to give a real-world example of how this is used and how I've used this hexagonal architecture in my day-to-day work. And then finally with some conclusion and then some time for questions hopefully. So starting with the intro. So hexagonal architecture is a software design pattern. It was first introduced by Alistair Coburn in the early 2000s. So this is not necessarily like a brand new topic. Maybe you've seen it before, and if so, maybe you've seen it referred to as ports and adapters, as you can kind of see with the visualization on your right-hand side there. One of the main pieces of this is it helps isolate your business logic from your external technologies, so the business logic can be implemented, updated, and tested completely separately from any external technologies you're using, maybe database, other APIs, things like that. Also, this idea of separation of concerns helps to create clear boundaries within your applications. So, for example, the business logic side, it doesn't care how your data is stored. So you can store it in, like, your SQL database, a NoSQL database, system, whatever, but having these areas separated really helps to have clear boundaries, and this is great as we're moving into this AI-assisted programming. So this hexagonal name comes from really how the architecture is shown graphically. It was just given this name to maybe have a more memorable name rather than ports in adapters so it wasn't really like a very specific reason and it just shows in a nice way by representing it as this hexagonal shape. This idea of, yeah, this shape doesn't really indicate that there's any specific number of ports and adapters in your system. That is just, of course, driven by the system design itself. So moving on to the core principles and concepts, I've zoomed in a little bit here on just one part of the hexagon that I was showing before on the previous slides. So yeah, in the center you have your domain. This is where you define your models based on your actual business context. So whatever you're specifically talking about. Then you have your port. So this is an interface that defines the contracts. I have a small example there of a very simple port for a general entity. In Python, these can be defined using abstract base classes, if you're familiar. And then kind of in the center of all this is your application core. So this is where you combine your business and domain logic with your ports. And we'll see more concrete examples later on of this. And then on the outer side is your adapters. So this is where you actually have your implementation of ports that handle all the external interactions. For example, in this case, your database adapter. So where you actually implement the functionality that you've defined in your port to connect and do all the operations on your database. Another key concept in hexagonal architecture is dependency inversion. So this is about your high-level modules should not depend on your low-level modules, but both should depend on abstractions. So here we can see we have, again, our entity port at the top. So this is our abstract base class. But now both our adapter inherits this class, and then the entity service itself depends on the more general entity port, not on the actual adapter itself. And then finally, when you go to create an instance of your entity service, that's when you're going to pass in your actual adapter that you've implemented. Moving forward to the benefits and some of the problems that hexagonal architecture can help solve, one of the really big things here is the improved maintainability by reducing coupling in your code. So as I kind of mentioned before, you can change your business logic in the application core without having to change other layers. Also, this allows for easier debugging and the ability to pinpoint errors to specific adapters. So if there's something that was wrong in your code that was specifically of how you're inserting data to your database, having this kind of design will allow you to see that it's OK, Yeah, it's the code directly related to this database operation. It's not some other functionality that the error is happening. It also helps enhance the testability of your system because you can separate your tests quite easily. So you can test just your business logic separately and then also your adapters, the actual implementations of what you're building can be tested separately as well. And another, I think one of the maybe nicest things about this is it allows for really easy technology migration. So if you started with your adapter as a SQL adapter for how your database is, so you might might have some create function there that has SQL alchemy code. Maybe something changed in the project, there was an infrastructure change or you decided, no, SQL is not the database I want to be using, I want to go with a NoSQL approach, then really all you have to do is create your new adapter for your NoSQL adapter, build whatever functionality you need there. So in this example, this create function, you would maybe have in some PyMongo code. And then, yeah, this only changes the adapter layer. You don't have to change a bunch of business logic as well, which is a really nice, I think, pro of this architecture. Also really related to this is once you've set up all the ports that you want to use in your system, if you are working on a team, you can, yeah, the adapters, the actual implementations can be worked on in parallel. So you could split that up across the team in a really nice way as well. Yeah, I think moving on here to the real world case study, I want to first start with just some general information what we do at RosenNext. So there's many areas of business at RosenNext that we work in. The couple that I've worked in are the first two, the upstream optimization. So this is related to improving production in oil fields. And then the other one is the waterline integrity. So this is inspecting water pipelines. We have some robots that we build and send down the water pipelines to collect data. There's several other areas of business here at RosenNext. For this example, though, we'll focus on upstream optimization a bit more. So just a little bit more background on that is we create sensors that can be installed on or can be installed in oil fields. So there's some visualization here roughly of what this might look like. These sensors collect data based on or related to the flow rate of oil, gas, and water. But what we require is from like internally is we need to be able to store and support data related to how these sensors are installed or as we refer to them as installations. So We need to know when was the sensor installed, which sensor it was that was installed, which customer it belongs to, and kind of all this information related to installations. And that will be the example we use kind of going through how hexagonal architecture is being used. So initial implementation might be like, okay, we're just going to create a class where we have all these functions to create an installation. And this is following a non-hexagonal architecture approach. So here you can see in the create function, we have the connection to the database, and then you'd have whatever operations to your database that you need. You might have some other API calls. And just overall, you're mixing all of your different functionality together into one function. And this maybe isn't the best approach when we have really clear separations of the different functionality we need. So we'll move into a more hexagonal approach for this. So starting at the kind of center of the diagram that I showed at the beginning of the presentation, we would start with the domain model. So in this case, this would be what does an installation actually mean in the business context? This is kind of the, maybe the most important step or one of the most important steps, right? Because this is how we define to our system what an installation actually is. So from this, you can see we have some ID for the installation, the device that belongs to the installation. so the device ID, where it's located, so that's given by the well ID and the field ID, who this belongs to, so what tenant or customer this belongs to, and then when did the installation actually start, and then at some point we'll have an end time of the installation. And what's really nice about this as well is this is just the domain model. This isn't necessarily how we need to define the model for storing the data in the database. That can be done separately later on in our adapter layer. It's not coupled directly to the domain model itself. Next, we would define our ports. So here is where we would define the functionality that we actually need. So these are just the functions. We aren't actually implementing them right now. So here we can see we have it called InstallationRepository. It could also be called InstallationPort, kind of whatever naming you want to use here. It doesn't matter so much. But, yeah, what does matter are the functions that you are creating. So we might need a function to get an installation by an ID and definitely a function to create a new installation. And, of course, this can be extended to whatever other functionality we need. If we need to delete installations, update them, and anything else. The next part that we would do is actually implement the adapters. So in this case, we have the, say, we have decided that, OK, our database is going to be SQL, so we'll have a SQL installation adapter that you can see on the right-hand side. And in this, we can have, now that we've inherited from our installation repository, or our installation port, basically, We have to define and implement all the functions that were defined in the port. So here is just an example of how we would define the create function. And this is really nice because this is only how we create the installation in the SQL database itself. This doesn't care about any other business logic or constraints that we might have on creating the installation. that will come we'll see that on kind of the next slide and i think one of the really great things about this is this allows for really easy understanding for an ai tool so i think obviously we we have shifted to this ai assisted programming in our day-to-day work and that is very much here to stay i would say and this uh yeah kind of breaking this down into smaller functionality I think allows for your AI assistant to help you just build these functions really easily. And it doesn't need to know so much about the actual business domain. So it doesn't care what installation is. It knows that this is just something that it needs to put into a database. next we have the application layer that kind of orchestrates all the ports together so this is where you can yeah include more of your ports so we only talked about the installation port for basically the database functionality but now we might have like a complete installation service and this is where we can combine the other ports that we need. So you can see again on the right-hand side some code example of this. So in the create function now, we have kind of whatever steps we need to actually create the installation. So if, for example, for the installation, we need to validate that the device exists Exists before actually creating the installation we can have maybe a device port that connects to a separate service That validates that the device actually exists before we put the device into our installation database Another example could be we see the user manager on the right hand side there Or there could be another step where we validate that the user actually has permissions to create the installation So whatever this kind of business logic that we need to follow can be created here in the application layer itself, not over in all the different adapters that we're building. And to show this kind of going back to the hexagonal architecture shape there on the left-hand side, I wanted to also just show quickly what this might look like in your repository. Tried to match the colors according to the diagram. So, yeah, you would maybe just have your source folder, And then within there, you could have your different folders for domain adapters and application. And then you can build out other subfolders within that as you need and structure your repository. Maybe in this way, but of course, this is very much up to the developer of how you want to be structuring your project. But just to give a more concrete example of what this might look like. so just moving into the last section here so in conclusion I would say that hexagonal architecture is really great for some of these reasons so helps isolate your business logic from your external technologies so we saw that in this case of creating installations right we can have the functionality for actually creating installation in the database over in our adapter but then all of the business logic that actually needs to be done before we can create the installation can be done in this like application layer by combining all of your different ports together that are needed and this I think follows very closely with the next point of this idea of separation of of concerns, leading to really clear boundaries in your code. So you don't need to, or yeah, this removes a lot of the coupling between your business logic and the data that you're handling. And just to reiterate, this combined can help make your code much more understandable and modifiable by AI tools. So yeah, I feel like this is a major benefit as we're moving into this new era. I've been working with this kind of structure in the repos I've been working on for the past several months. And I've noticed that it's really, really useful. And the LLM, or Claude in my case, is able to just help write a lot of code for me. And it works very well when we can separate the whole business or domain from the actual functionality, because the functions like creating something in a database, that's a very standard functionality that exists in many applications. But the more intricate domain part is really what separates all the different applications that everybody in this room works on. One maybe drawback to this hexagonal architecture that I wanted to mention is that this can lead to more time at the beginning of development to design your system. So I think this part of really defining your domain is really important. But in general, it's going to take maybe a bit more time upfront to design that and kind of think through, OK, what are all the different ports that I'm going to need and the different functionality? But I think it can help lead to a much better development process down the line. So a bit more time up front. But yeah, also hexagonal architecture, it might not be necessary for every system. So I think it's not a kind of one-size-fits-all solution, but kind of another tool to add to our development toolboxes as we continue building software in the future. I think, oh, yes. So some further resources that I wanted to mention. So the original article on hexagonal architecture is linked here. The slides are shared on the conference website, so you could grab that if you wanted to see the original article. It's from 2005. Then there's some other articles listed that I thought were really useful, specifically this first one. It provides a really intuitive analogy. It's from CodeCentric, which is a company we work with at Rosen Next, and provides a great analogy of hexagonal architecture being an island and kind of gives you a different way to think about hexagonal architecture. And then just a couple other articles that I found pretty useful when I was learning more about the topic and continue learning about it. I think at this time we have some time for questions. I'll leave this up behind me. This was a question I asked to Claude and to get its thoughts on hexagonal architecture and if it's a good solution for Claude. So yes, thank you, everybody.

Speaker 1 [23:35]

Thank you so much, Luke, for the great talk. And now we also have some recommendation from Claude about it. I'll just take some questions that we have on the Q&A. I did feel, I guess, some echoes of dependency inversion principle, like solid design principle or like Martin Flower's clean architecture principle in this. So that's really reassuring that in some ways these things are leading to the same path. So the first question is, do you think this architecture is also a good pattern for designing multi-agent systems with agents interacting with application layers?

Speaker 2 [24:24]

Yeah, definitely. I think that would be a perfect case because, yeah, you could have, yeah, I think as we saw this morning in some example of the different agents working together, that could be something done in here as well. you could have maybe an agent that is very much responsible for your database functionality and then whatever other kind of ports that you might have through your application. So maybe an agent that is responsible for interactions with third-party APIs or other internal APIs and things of that nature.

Speaker 1 [25:08]

Okay. Thank you. And do you think, does it also make sense to do this for a single repo, probably a mono repo, where, like, it's just gotten started, they're asking for every change made at the core, you would also need to change the core code, and every hexagonal adapter, and so forth? Yeah, yeah.

Speaker 2 [25:29]

Yeah, yeah, it can lead to some times where you have to change things in multiple places, which can be maybe a little frustrating if, yeah, you didn't design your port completely right the first time, which, of course, is going to happen. There will always be cases where you need some new functionality brought in. But I think still, even though these updates will be necessary maybe to make in multiple places, it's still a really useful tool to design your repo. And I think also if it's a monolith repo, it kind of applies in that way as well.

Speaker 1 [26:08]

That also leads us to the next question. Since there is no free lunch, what are the cons of this?

Speaker 2 [26:14]

Yeah, yeah, it's definitely true. I think kind of what I was mentioning at the end with this, yeah, there's maybe a lot of more boilerplate code that needs to be defined up front, but I think, as we can all imagine, your AI assistant might be able to help you with that really well once you've defined these domain models that you need to. So in our case, I talked about installations, But, yeah, you could have many different types of models that are in your domain. And starting from there, your AI assistant can hopefully help even generate a lot of the boilerplate code that you might need as well. And, yeah, I think probably other drawbacks as well. But, yeah, I'll leave it at that.

Speaker 1 [27:11]

So defining ports for a domain entity can be simple for create, read, update, and delete use cases. But for example, a search endpoint can easily expose details of the adapter on port. How do you basically keep your definitions clean?

Speaker 2 [27:30]

Yeah, it's a really good question. I think, yeah, that's part of the trick to this is trying to find ways to do that. I would say, yeah, just being able to kind of think of the functionality in the simplest terms and maybe most general terms of what you need and being able to build your functions in that way where they're quite generalizable and then really leaving the more specific implementation in your adapter layer. That way, if, for example, you, like I mentioned with changing your database technology, then you're not so coupled to how you've defined something in the port, but it's really quite generalizable that it can be implemented whether you're using a SQL database or some other database technology.

Speaker 1 [28:31]

Interesting. And I guess next question is, how is it related to clean architecture? I think they're referring to Robert Martin, is it? Robert Martin's clean architecture, and what is the key difference?

Speaker 2 [28:46]

Yeah, I think that's a really good question. I will say, I would leave that to some further discussion. I'm not as familiar with clean architecture in the complete implementation of that, so I would say I would need to check more there.

Speaker 1 [29:09]

Yeah, I guess like apart from clean architecture, onion architecture also comes

Speaker 2 [29:14]

Yeah, I think when I was just reading more about hexagonal architecture, you see this onion architecture as well. And I think overall, they're very similar in some ways. But yeah, maybe this hexagonal architecture is more of an abstraction from there.

Speaker 1 [29:42]

And I'll take a few more questions. So in many applications, examples, Spring Boot from Java, even in Python, we often use patterns like repository abstraction and dependency injection to decouple business logic from implementation. How does hexagon architecture relate to these patterns? Is this essentially the same idea or does it introduce stricter boundaries or additional structure that might not be present?

Speaker 2 [30:11]

No, no, not really. I think that's very similar to what was mentioned in the question of splitting this business logic from your core functionality or implementations.

Speaker 1 [30:12]

We'll be right back.

Speaker 2 [30:29]

So yeah, I think maybe you're already kind of doing hexagonal architecture in some way, but didn't know the maybe term that was exactly being used for that. So.

Speaker 1 [30:43]

Do you generally use AI to create or refactor your architecture? I think you already answered it in some way.

Speaker 2 [30:50]

Yeah, yeah, yeah, definitely. Yeah, I think it's super useful to be able to use AI in this work.

Speaker 1 [30:51]

Yeah, yeah.

Speaker 2 [31:01]

And I think in doing this architecture or designing a system with this hexagonal architecture in mind allows for really easy use of the AI tools for refactoring or doing a lot of the maybe more mundane tasks that you don't want to actually be spending time doing. Thank you.

Speaker 1 [31:21]

I guess if you have also split your files appropriately, it also results in less token usage. So probably savings in that front as well. Yeah.

Speaker 2 [31:32]

Yeah, yeah, yeah.

Speaker 1 [31:35]

And probably just the final question, what are your experiences on migrating existing applications to hexagonal architecture? Any tips?

Speaker 2 [31:44]

Yeah, good question. I've been fortunate that I joined a new project in the past six months, so I was able to kind of just start using hexagonal architecture from the beginning. But yeah, I think overall, yeah, it may be some work to shift to this architecture, but I think in general it will help to solve a lot of problems further down the line that you might run into. So definitely some overhead to if you have an existing repo to shift it into this structure, but For my experience I would say It would be worthwhile

Speaker 1 [32:24]

Thank you so much, Luke, and thank you so much for joining.

Luke Gerstner

About — in the speaker's own words

I started my career as a data scientist in the oil and gas industry, where I worked on building services to deploy machine learning models in a production environment. Currently, I work as a software engineer at Rosenxt on a cloud backend team building a multi-tenant data management system. I am passionate about the combination of data science and software engineering and continuing to grow in both fields.

Social card for talk: Leveraging Hexagonal Architecture When Building Applications