<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jose Javier Sanahuja</title>
    <description>The latest articles on DEV Community by Jose Javier Sanahuja (@josejaviersanahuja).</description>
    <link>https://dev.to/josejaviersanahuja</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F840741%2F5c343208-b81d-491e-bfc1-62343feccc72.jpeg</url>
      <title>DEV Community: Jose Javier Sanahuja</title>
      <link>https://dev.to/josejaviersanahuja</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/josejaviersanahuja"/>
    <language>en</language>
    <item>
      <title>Beyond CRUD: Decoupling with Hexagonal Architecture and Events (Without Losing Your Mind)</title>
      <dc:creator>Jose Javier Sanahuja</dc:creator>
      <pubDate>Tue, 29 Jul 2025 22:47:16 +0000</pubDate>
      <link>https://dev.to/josejaviersanahuja/beyond-crud-decoupling-with-hexagonal-architecture-and-events-without-losing-your-mind-5h8i</link>
      <guid>https://dev.to/josejaviersanahuja/beyond-crud-decoupling-with-hexagonal-architecture-and-events-without-losing-your-mind-5h8i</guid>
      <description>&lt;h2&gt;
  
  
  Introduction: Beyond CRUD - Our Journey to a Resilient Hexagon
&lt;/h2&gt;

&lt;p&gt;Let's be honest. When you dive into &lt;strong&gt;Hexagonal Architecture&lt;/strong&gt; tutorials, it often feels like Groundhog Day. How many times have we seen the 'CreateUserUseCase' with its trusty 'UserRepository'? Don't get me wrong, these examples are fantastic for grasping the core concepts of &lt;strong&gt;Ports&lt;/strong&gt; and &lt;strong&gt;Adapters&lt;/strong&gt;. They show us how to keep our precious domain logic isolated and pristine. But let's face it: the real world isn't a simple CRUD operation where everything flows synchronously from a single HTTP request.&lt;/p&gt;

&lt;p&gt;Before we go further, stop and think about the last project you opened. What did its file structure scream at you? Did it scream "I AM A SPRING BOOT APP!" with its &lt;code&gt;pom.xml&lt;/code&gt; and framework-specific annotations everywhere? Or did it scream "I AM A RESERVATION SYSTEM!" or "I AM A NOTIFICATION ENGINE!"? This isn't just semantics; it's the heart of building resilient software. An architecture that screams its purpose—&lt;strong&gt;what it does, not what it uses&lt;/strong&gt;—is an architecture that survives. When your shiny framework dies or becomes obsolete (and it will), you simply swap out an adapter; you don't have to perform a heart transplant on the soul of your application.&lt;/p&gt;

&lt;p&gt;That's the philosophy we're embracing today. In this post, we're cutting through the noise and diving deep into a more realistic, and frankly, more exciting application of Hexagonal Architecture: building an &lt;strong&gt;asynchronous notification system&lt;/strong&gt;. We'll explore how &lt;strong&gt;events&lt;/strong&gt; can act as primary drivers, initiating actions within our application core without direct user intervention. More importantly, we'll unravel the intricate dance of &lt;strong&gt;secondary adapters&lt;/strong&gt; – not just your garden-variety database persistence, but external services like email, SMS, push notifications, templating engines, and even HTML validators. By the end, you'll see how embracing these concepts allows us to create truly resilient, testable, and maintainable systems that adapt to change without losing their minds (or yours!).&lt;/p&gt;

&lt;p&gt;To make these ideas tangible, we'll be dissecting a real-world notification domain. Think of it as a crucial, transversal component in any modern application. This isn't a hypothetical setup; it's a blueprint inspired by battle-tested systems, showing how a complex domain can be structured using Hexagonal Architecture. Get ready to peek under the hood and see how notification requests are processed, rendered, and dispatched across multiple channels, all while keeping the core business logic delightfully decoupled from the technology that powers it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Alright, Theory's Over. Let's Map This to Real Code
&lt;/h2&gt;

&lt;p&gt;That was the textbook definition. Now, let's see how &lt;strong&gt;Hexagonal Architecture&lt;/strong&gt; translates to an actual directory structure. Instead of abstract boxes and arrows, we have concrete files and folders. This is where the architecture truly starts to "scream" its purpose, telling you what the system &lt;em&gt;does&lt;/em&gt; rather than what technologies it &lt;em&gt;uses&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here's the simplified layout of our &lt;code&gt;notification&lt;/code&gt; domain module, which we'll dissect piece by piece. This structure is a direct reflection of the Hexagonal principles we just discussed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/notification
├── port
│   ├── &lt;span class="k"&gt;in&lt;/span&gt;
│   │   └── SendNotificationUseCase.java
│   └── out
│       ├── NotificationSenderPort.java
│       ├── NotificationRepositoryPort.java
│       └── RendererPort.java
├── domain
│   ├── model
│   │   ├── Notification.java
│   │   ├── TemplateCode.java  &amp;lt;&lt;span class="nt"&gt;--&lt;/span&gt; A beautiful Value Object!
│   │   └── ... &lt;span class="o"&gt;(&lt;/span&gt;other pure domain models&lt;span class="o"&gt;)&lt;/span&gt;
│   └── exceptions
│       └── ... &lt;span class="o"&gt;(&lt;/span&gt;custom business exceptions&lt;span class="o"&gt;)&lt;/span&gt;
├── application
│   └── NotificationApplicationService.java
└── adapter
    ├── sender
    │   ├── EmailNotificationSenderAdapter.java
    │   └── SmsNotificationSenderAdapter.java
    ├── persistence
    │   └── jOOQNotificationRepositoryAdapter.java
    └── renderer
        └── ThymeleafRendererAdapter.java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down each part and how it aligns with the Hexagonal paradigm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The &lt;code&gt;port&lt;/code&gt; directory&lt;/strong&gt; is your Hexagon's clear boundary.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;port/in&lt;/code&gt; directory holds your &lt;strong&gt;Inbound Ports&lt;/strong&gt;. In our case, &lt;code&gt;SendNotificationUseCase.java&lt;/code&gt; is an interface that defines precisely what our application's core &lt;strong&gt;can do&lt;/strong&gt;: send a notification.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;port/out&lt;/code&gt; directory contains your &lt;strong&gt;Outbound Ports&lt;/strong&gt;. These are interfaces like &lt;code&gt;NotificationSenderPort&lt;/code&gt; or &lt;code&gt;NotificationRepositoryPort&lt;/code&gt; that define what our application's core &lt;strong&gt;needs from the outside world&lt;/strong&gt;: a way to send notifications and a way to save them.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;The &lt;code&gt;domain&lt;/code&gt; directory&lt;/strong&gt; is the crown jewel. This is your pure core. It contains all the essential business logic and models, completely independent of any technical details. Notice &lt;code&gt;domain/model/TemplateCode.java&lt;/code&gt;. This isn't just a &lt;code&gt;String&lt;/code&gt;; it's a &lt;strong&gt;Value Object&lt;/strong&gt;. It encapsulates crucial business rules, like perhaps a template must have a way to say if it has all the requiredVariables. This is real business logic, right here in a tiny, pure, and easily testable object.&lt;br&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// notifications/domain/model/Template.java&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Template&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other fields ...&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requiredVariables&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other fields ...&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Checks if the provided map of variables is sufficient to render the template.
     * This is a core business rule.
     * @param variables The map of variables provided for rendering.
     * @return true if all required variables are present, false otherwise.
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;hasSufficientVariables&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;variables&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// A null map of variables is never sufficient.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;variables&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// If the template requires no variables, the condition is always met.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requiredVariables&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requiredVariables&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// The business rule: Does the provided map of variables&lt;/span&gt;
        &lt;span class="c1"&gt;// contain all the keys that this template requires?&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;requiredVariables&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;allMatch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;variables:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;containsKey&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The &lt;code&gt;application&lt;/code&gt; directory&lt;/strong&gt; is the orchestrator. &lt;code&gt;NotificationApplicationService.java&lt;/code&gt; is the concrete implementation of our inbound port (&lt;code&gt;SendNotificationUseCase&lt;/code&gt;). It coordinates the work: it takes a command, leverages the domain models to perform business logic, and then calls &lt;strong&gt;outbound ports&lt;/strong&gt; to get things done (like saving the notification via &lt;code&gt;NotificationRepositoryPort&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The &lt;code&gt;adapter&lt;/code&gt; directory&lt;/strong&gt; is where reality hits. This is where we implement the outbound ports, connecting our core to specific technologies. &lt;code&gt;jOOQNotificationRepositoryAdapter&lt;/code&gt; knows how to talk to a PostgreSQL database using jOOQ. &lt;code&gt;EmailNotificationSenderAdapter&lt;/code&gt; knows how to connect to an SMTP server. The beauty? The core doesn't know or care about jOOQ or SMTP; it just talks to the &lt;code&gt;NotificationRepositoryPort&lt;/code&gt; or &lt;code&gt;NotificationSenderPort&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The Golden Rule: Conventions That Set You Free
&lt;/h3&gt;

&lt;p&gt;At first, all these names (UseCase, Port, Service, Adapter) can feel like a bit much. It's a new convention, and it takes a minute to get used to. But it pays off by creating a crystal-clear, self-documenting structure. The golden rule for a clean Hexagonal setup is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;Inbound Port&lt;/strong&gt; (e.g., &lt;code&gt;SendNotificationUseCase&lt;/code&gt;) is an interface located in &lt;code&gt;port/in&lt;/code&gt;. It is implemented by an &lt;strong&gt;Application Service&lt;/strong&gt; in the &lt;code&gt;application&lt;/code&gt; layer.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;Outbound Port&lt;/strong&gt; (e.g., &lt;code&gt;NotificationRepositoryPort&lt;/code&gt;) is an interface located in &lt;code&gt;port/out&lt;/code&gt;. It is implemented by an &lt;strong&gt;Adapter&lt;/strong&gt; in the &lt;code&gt;adapter&lt;/code&gt; layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stick to this, and you've won half the battle. This simple convention is what gives you the superpowers of a truly decoupled application.&lt;/p&gt;




&lt;h3&gt;
  
  
  So, Why All This Fuss? The Glorious Payoff
&lt;/h3&gt;

&lt;p&gt;Following this structure isn't just for academic brownie points or chasing buzzwords. It gives you tangible, game-changing benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;INSANE TESTABILITY:&lt;/strong&gt; You can test your entire application logic (&lt;code&gt;NotificationApplicationService&lt;/code&gt; + Domain objects) without a single external dependency. Just mock the outbound port interfaces. Your tests become lightning-fast, ultra-reliable, and they test &lt;em&gt;your code&lt;/em&gt;, not whether your database is having a bad day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TECHNOLOGY MIGRATION FOR DAYS:&lt;/strong&gt; Woke up and decided you hate PostgreSQL and want to use MongoDB? Fine. Write a new &lt;code&gt;MongoNotificationRepositoryAdapter&lt;/code&gt; that implements &lt;code&gt;NotificationRepositoryPort&lt;/code&gt;, change one line in your configuration, and you're done. The application core doesn't even notice the seismic shift. You're swapping out LEGO bricks, not performing open-heart surgery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRYSTAL CLEAR BUSINESS RULES (NO SPAGHETTI!):&lt;/strong&gt; The business logic lives purely in the &lt;code&gt;domain&lt;/code&gt;. The orchestration of that logic happens in the &lt;code&gt;application&lt;/code&gt; layer. The messy technical details are confined to the &lt;code&gt;adapters&lt;/code&gt;. There's no ambiguity. You, your team, and the new hire who starts next month will know exactly where to look for what. It's the ultimate spaghetti code repellent.&lt;/li&gt;
&lt;/ul&gt;







&lt;h2&gt;
  
  
  The Unexpected Twist: The Event as a Primary Adapter
&lt;/h2&gt;

&lt;p&gt;So far, we've implicitly considered primary adapters as things like &lt;strong&gt;REST controllers&lt;/strong&gt; that receive HTTP requests. That's certainly true, and it's a very common way to drive a Hexagonal application. But what happens when an action isn't triggered by a direct external request? What if a significant event occurs &lt;em&gt;within&lt;/em&gt; your application – say, an &lt;code&gt;OrderPlacedEvent&lt;/code&gt; – and this event needs to trigger further processing, like sending a confirmation notification?&lt;/p&gt;

&lt;p&gt;This is where the true power of &lt;strong&gt;Hexagonal Architecture&lt;/strong&gt;, combined with an &lt;strong&gt;event-driven mindset&lt;/strong&gt;, really shines. Instead of tightly coupling your order service to your notification service, or creating awkward direct calls, we can introduce an &lt;strong&gt;Event Listener as a Primary Adapter&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Meet our hero for this scenario: &lt;code&gt;NotificationSpringEventListener&lt;/code&gt;. This adapter ensures our notification core remains decoupled while reacting to relevant application events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// notification/adapter/event/NotificationSpringEventListener.java&lt;/span&gt;

&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt; &lt;span class="c1"&gt;// Lombok for constructor injection, nice and clean!&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationSpringEventListener&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;SendNotificationUseCase&lt;/span&gt; &lt;span class="n"&gt;sendNotificationUseCase&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;NotificationMapper&lt;/span&gt; &lt;span class="n"&gt;notificationMapper&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// To map the event data to a command&lt;/span&gt;

    &lt;span class="nd"&gt;@Async&lt;/span&gt; &lt;span class="c1"&gt;// Crucial: We don't want to block the thread that published the event!&lt;/span&gt;
    &lt;span class="nd"&gt;@EventListener&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;handleNotificationEvent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;NotificationEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Here you might add logging, tracing, or metrics&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received NotificationEvent for user: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;recipientId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// Example log&lt;/span&gt;

        &lt;span class="c1"&gt;// The adapter's job: translate the external event into an internal command&lt;/span&gt;
        &lt;span class="nc"&gt;SendNotificationCommand&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notificationMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toCommand&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Drive the core! The use case doesn't know this came from an event.&lt;/span&gt;
        &lt;span class="n"&gt;sendNotificationUseCase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendNotification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;NotificationSpringEventListener&lt;/code&gt; is a &lt;strong&gt;Primary Adapter&lt;/strong&gt;. It's actively &lt;em&gt;listening&lt;/em&gt; for something – a &lt;code&gt;NotificationEvent&lt;/code&gt; published by another part of your application. For instance, imagine a &lt;code&gt;VerificationCodeForTwoFactorAuthCreated&lt;/code&gt; event being published after a user requests a new 2FA code. Our listener picks this up and acts accordingly.&lt;/li&gt;
&lt;li&gt;When it "hears" an event, it acts as the &lt;strong&gt;entry point&lt;/strong&gt; into our notification hexagon. It takes the data from the event and transforms it into a &lt;code&gt;SendNotificationCommand&lt;/code&gt; – a format that our core &lt;code&gt;SendNotificationUseCase&lt;/code&gt; understands.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;@Async&lt;/code&gt; annotation is vital here. It ensures that publishing the &lt;code&gt;NotificationEvent&lt;/code&gt; doesn't block the original thread (e.g., the thread creating the 2FA code). Our notification sending process runs in the background, keeping the main flow responsive.&lt;/li&gt;
&lt;li&gt;Crucially, your &lt;code&gt;SendNotificationUseCase&lt;/code&gt; (the &lt;strong&gt;Inbound Port&lt;/strong&gt;) remains completely oblivious to &lt;em&gt;how&lt;/em&gt; it was invoked. It doesn't know if a REST controller called it, a CLI command, or an event listener. It just receives a command and executes the business logic. This is the essence of decoupling!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern allows your application to react to internal state changes or events in a highly decoupled manner, making your system more flexible and resilient. It opens up a world of possibilities beyond simple request-response flows.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Orchestra of Minions: Secondary Adapters in Action
&lt;/h2&gt;

&lt;p&gt;If our event listener was the driving force, the &lt;strong&gt;secondary (or driven) adapters&lt;/strong&gt; are the components that are put to work by our application core. They are the concrete implementations of our &lt;strong&gt;outbound ports&lt;/strong&gt;—the promises our application core needs fulfilled by the outside world.&lt;/p&gt;

&lt;p&gt;Most tutorials stop at the classic &lt;code&gt;UserRepository&lt;/code&gt; implementation when discussing driven adapters. That's fine, but the real world is a chaotic orchestra of databases, third-party APIs, rendering engines, and more. Our notification domain is the perfect stage to showcase this diversity, demonstrating that secondary adapters are far more than just data access objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Classic: The Persistence Adapter
&lt;/h3&gt;

&lt;p&gt;Let's get the obvious one out of the way. Our application needs to save the state of a notification, so it requires a persistence mechanism.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Port (&lt;code&gt;port/out&lt;/code&gt;): &lt;code&gt;NotificationRepositoryPort.java&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;NotificationRepositoryPort&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Adapter (&lt;code&gt;adapter/persistence&lt;/code&gt;): &lt;code&gt;DatabaseNotificationRepositoryAdapter.java&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This class would contain all the jOOQ, JPA, or JDBC logic needed to talk to our PostgreSQL database. The core doesn't care &lt;em&gt;how&lt;/em&gt; it's done, only that the &lt;code&gt;save&lt;/code&gt; and &lt;code&gt;findById&lt;/code&gt; contracts are fulfilled. This keeps your domain blissfully ignorant of your chosen database technology.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Messengers: The Sender Adapters
&lt;/h3&gt;

&lt;p&gt;Here's where it gets more interesting. A notification isn't useful until it's sent. Sending an email is a fundamentally different technical process than sending an SMS, or a push notification. Each requires a distinct external interaction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Port (&lt;code&gt;port/out&lt;/code&gt;): &lt;code&gt;NotificationSenderPort.java&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;NotificationSenderPort&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Channel&lt;/span&gt; &lt;span class="nf"&gt;getSupportedChannel&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// e.g., EMAIL, SMS, PUSH&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Adapters (&lt;code&gt;adapter/sender&lt;/code&gt;):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;EmailNotificationSenderAdapter.java&lt;/code&gt;&lt;/strong&gt;: Implements &lt;code&gt;NotificationSenderPort&lt;/code&gt;. It knows how to connect to an SMTP server or a third-party API like SendGrid. It probably uses a library like JavaMail or a specific SDK.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SmsNotificationSenderAdapter.java&lt;/code&gt;&lt;/strong&gt;: Also implements &lt;code&gt;NotificationSenderPort&lt;/code&gt;. This one knows how to talk to a messaging API like Twilio.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(And perhaps &lt;code&gt;PushNotificationSenderAdapter.java&lt;/code&gt;, &lt;code&gt;WebSocketNotificationSenderAdapter.java&lt;/code&gt;, etc.)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The beauty is that the &lt;code&gt;ApplicationService&lt;/code&gt; just asks the port to send. It has no idea about SMTP, Twilio, or specific push notification protocols. It just speaks the language of the &lt;code&gt;NotificationSenderPort&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Artist: The Template Renderer Adapter
&lt;/h3&gt;

&lt;p&gt;Notifications often use dynamic content, which means they rely on HTML templates that need data injected into them. Rendering that HTML is an external concern, handled by a specific library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Port (&lt;code&gt;port/out&lt;/code&gt;): &lt;code&gt;TemplateRendererPort.java&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TemplateRendererPort&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TemplateCode&lt;/span&gt; &lt;span class="n"&gt;templateCode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;variables&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Adapter (&lt;code&gt;adapter/renderer&lt;/code&gt;): &lt;code&gt;ThymeleafTemplateRendererAdapter.java&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This adapter implements the port by using the Thymeleaf library to process a template file and inject variables. If you wanted to switch to a different engine like Handlebars or FreeMarker tomorrow, you'd just write a new &lt;code&gt;HandlebarsTemplateRendererAdapter&lt;/code&gt; (or similar) and swap the implementation in your configuration. The core application wouldn't change at all, demonstrating powerful flexibility.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Pro Move: The Composite Adapter
&lt;/h3&gt;

&lt;p&gt;This is where you can really flex the power of the Hexagonal pattern and polymorphism. What if you want to allow your &lt;code&gt;ApplicationService&lt;/code&gt; to send a notification without knowing the specific channel (email, SMS, push) upfront? You can use a &lt;strong&gt;Composite Adapter&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Adapter (&lt;code&gt;adapter/sender&lt;/code&gt;): &lt;code&gt;CompositeNotificationSenderAdapter.java&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This special class also implements &lt;code&gt;NotificationSenderPort&lt;/code&gt;, but it doesn't send anything itself. Instead, it holds a collection of all other &lt;code&gt;NotificationSenderPort&lt;/code&gt; implementations and delegates the work to the correct one based on the notification's channel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// notification/adapter/sender/CompositeNotificationSenderAdapter.java&lt;/span&gt;

&lt;span class="nd"&gt;@Component&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"compositeSender"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// A cool name for the bean!&lt;/span&gt;
&lt;span class="nd"&gt;@Primary&lt;/span&gt; &lt;span class="c1"&gt;// Tells Spring to inject this one by default when NotificationSenderPort is requested&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CompositeNotificationSenderAdapter&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;NotificationSenderPort&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Channel&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;NotificationSenderPort&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;senders&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Spring cleverly injects a list of all beans that implement NotificationSenderPort&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CompositeNotificationSenderAdapter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NotificationSenderPort&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;senderList&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;senders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;senderList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSupportedChannel&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Exclude the composite itself if it were in the list&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;NotificationSenderPort:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getSupportedChannel&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Here's the magic: we find the appropriate sender and delegate.&lt;/span&gt;
        &lt;span class="c1"&gt;// The use case remains completely oblivious to this routing complexity.&lt;/span&gt;
        &lt;span class="nc"&gt;NotificationSenderPort&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;senders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getChannel&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// It's good practice to handle unsupported channels, e.g., log or throw&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UnsupportedOperationException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No sender available for channel: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getChannel&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Channel&lt;/span&gt; &lt;span class="nf"&gt;getSupportedChannel&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// This composite doesn't represent a single channel; it's a router.&lt;/span&gt;
       &lt;span class="c1"&gt;// Returning null or throwing an exception here is common for composites.&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is freedom. This is power. Your &lt;code&gt;ApplicationService&lt;/code&gt; can now be injected with a single &lt;code&gt;NotificationSenderPort&lt;/code&gt; (the composite) and remain completely ignorant of the complex routing logic and the multitude of sender implementations. It simply says, "Hey, &lt;code&gt;NotificationSenderPort&lt;/code&gt;, send this &lt;code&gt;Notification&lt;/code&gt;." The composite then figures out the correct technical adapter.&lt;/p&gt;

&lt;p&gt;With all these pieces in place—the event-driven primary adapter and the rich orchestra of secondary adapters—you have a system that is robust, flexible, and ridiculously easy to test and maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bringing It All Home: Your Hexagonal Superpowers 🦸‍♂️
&lt;/h2&gt;

&lt;p&gt;We started this journey by throwing some well-deserved shade at the overly simplistic "Hello, World" examples of Hexagonal Architecture. We argued that the real world is a chaotic mess of asynchronous events, a zoo of external services, and ever-changing business requirements. A simple, synchronous CRUD example just doesn't cut it.&lt;/p&gt;

&lt;p&gt;And what did we do? We systematically built a system that tames that chaos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We put our business logic in a protected &lt;strong&gt;Domain Core&lt;/strong&gt;, a fortress of pure, technology-agnostic rules.&lt;/li&gt;
&lt;li&gt;We introduced an &lt;strong&gt;Event Listener as a Primary Adapter&lt;/strong&gt;, proving our application can react to its own internal drama, not just pokes from the outside world.&lt;/li&gt;
&lt;li&gt;We assembled an &lt;strong&gt;army of diverse Secondary Adapters&lt;/strong&gt;—for databases, email APIs, SMS gateways, template engines, and more—to handle all the messy, real-world integration details.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But what's the ultimate prize? Why go through all this trouble of defining ports and writing adapters?&lt;/p&gt;

&lt;h3&gt;
  
  
  You Build Future-Proof Software
&lt;/h3&gt;

&lt;p&gt;Your application's identity is its purpose (a Notification System), not its tools (a Spring Boot app). When the next hot framework or cloud service comes along, you won't have a panic attack. You'll write a new adapter, plug it in, and have a celebratory well deserved drink. Your core logic, the most valuable part of your system, remains untouched and resilient.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Gain Ludicrous Testability
&lt;/h3&gt;

&lt;p&gt;You can test the absolute of your core business logic without a database, a mail server, or a full moon. Your unit tests are lightning-fast, ultra-reliable, and tell you if your logic is sound, not if your network connection is flaky.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Embrace Change Instead of Fearing It
&lt;/h3&gt;

&lt;p&gt;The business wants to add Slack notifications next quarter? You're not going to groan and schedule a three-week refactoring nightmare. You're going to say, "No problem," write a &lt;code&gt;SlackNotificationSenderAdapter&lt;/code&gt;, and look like the hero you are. The system is designed for evolution.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Hexagonal Architecture&lt;/strong&gt;, especially when supercharged with an event-driven mindset, isn't just an academic pattern. It's a practical, battle-tested strategy for writing professional, clean, and adaptable backend systems. It’s how you stop fighting your code and start building software that lasts.&lt;/p&gt;

&lt;p&gt;Now go on, build something awesome!&lt;/p&gt;

</description>
      <category>eventdriven</category>
      <category>architecture</category>
      <category>domaindrivendesign</category>
      <category>hexagonalarchitecture</category>
    </item>
    <item>
      <title>Taking Control with PostgreSQL Functions: Closing the Gap to ORM Functionality</title>
      <dc:creator>Jose Javier Sanahuja</dc:creator>
      <pubDate>Thu, 08 Jun 2023 16:02:48 +0000</pubDate>
      <link>https://dev.to/josejaviersanahuja/taking-control-with-postgresql-functions-closing-the-gap-to-orm-functionality-148b</link>
      <guid>https://dev.to/josejaviersanahuja/taking-control-with-postgresql-functions-closing-the-gap-to-orm-functionality-148b</guid>
      <description>&lt;h2&gt;
  
  
  Unveiling the Disparity: Understanding the Divide Between Direct Driver and ORM Functionality
&lt;/h2&gt;

&lt;p&gt;When it comes to choosing the technologies for developing a backend and manipulating data in a database like PostgreSQL, developers often face the choice between using the official driver or an abstraction layer like an ORM, in this case, Prisma. Both approaches have their advantages and disadvantages, but it has been argued that ORMs are superior due to a myriad of features designed for the common CRUD operations typically performed in a REST API. In this article, we will explore how functions in PostgreSQL allow us to bridge the gap between using the official driver directly and the functionalities provided by an ORM like Prisma. We will specifically focus on the data insertion operations, examining how functions can deliver results similar to those achieved when using an ORM, including the ability to return created objects and much more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_PASS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;db connected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`INSERT INTO reservations (_date, hora, res_number, res_name, room, meal_plan, pax_number, cost, observations)
      VALUES
        ('2023-07-27', '21:00', 001, 'Jhon Doe', 'P01', 'SC', 2, 50.00, 'Sin observaciones')`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disconnected from db&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="cm"&gt;/*
db connected
Result {
  command: 'INSERT',
  rowCount: 1,
  oid: 0,
  rows: [],
  fields: [],
  ... more metada
}
disconnected from db
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;In this example run in Nodejs, you can see that there aren't many useful information on the result of that operation and too much metadata.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, let's compare it with an insert run in Prisma.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pokemons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new_pokemon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="cm"&gt;/*
{ id: 1100, name: 'new_pokemon' }
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;With Prisma, We can get rid of the metadata and extract the object inserted in the database&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's try to emulate that type of result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intorduction to function in Postgresql.
&lt;/h2&gt;

&lt;p&gt;Let's jump directly to a simple code example of a function in Postgres.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num1&lt;/span&gt; &lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num2&lt;/span&gt; &lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;-- declare function and parameteres&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="nb"&gt;numeric&lt;/span&gt; &lt;span class="c1"&gt;-- returned type&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="c1"&gt;-- starting the code&lt;/span&gt;
  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;num1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;num2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- result&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- end of code&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- end of the function&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;-- returns 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There could be more sections on a function declaration, like catching exceptions or declaring variables. We'll leave that for later.&lt;/p&gt;

&lt;p&gt;But now let's dive into another example. A bit more elaborate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;pax&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;do_res&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- date of reservation&lt;/span&gt;
  &lt;span class="n"&gt;to_res&lt;/span&gt; &lt;span class="nb"&gt;TIME&lt;/span&gt; &lt;span class="c1"&gt;-- time of reservation&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- populating database with bad data that will throw exceptions in our function&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;do_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2022-01-01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'10:00:00'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* Use it to play with the function if needed
-- populating database with good data
INSERT INTO reservations (pax, capacity, do_res, to_res) VALUES 
(2, 4, '2022-01-01', '10:00:00'),
(3, 6, '2022-01-02', '11:00:00'),
(4, 8, '2022-01-03', '12:00:00'),
(5, 10, '2022-01-04', '13:00:00'),
(6, 12, '2022-01-05', '14:00:00'),
(7, 14, '2022-01-06', '15:00:00'),
(8, 16, '2022-01-07', '16:00:00'),
(9, 18, '2022-01-08', '17:00:00'),
(10, 20, '2022-01-09', '18:00:00');
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets try to create a function that can tell the porcentage of ocupation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;ocupation&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This is a normal query but now let's generate the function&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I know we could use a NULLIF to avoid exceptions, but i will want to throw this exception soon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;porcentage_ocupation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fecha_i&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fecha_f&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt; &lt;span class="c1"&gt;-- declaring variables&lt;/span&gt;
  &lt;span class="n"&gt;total_pax&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;total_capacity&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;porcentage&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pax&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;total_pax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_capacity&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;do_res&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="n"&gt;fecha_i&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;fecha_f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;porcentage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;total_pax&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total_capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;porcentage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Now we have created a more complex function but this is still not enough&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It is very common and a good practice to let the servers handle any type of errors. If an error occurs inside the database, it will scalate to the server throwing an exception. If you populate the server only with the bad data left behind, and try to run the function this is what you will get.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;porcentage_ocupation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'2022-01-01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2022-12-31'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- ERROR:  division by zero&lt;/span&gt;
&lt;span class="c1"&gt;-- CONTEXT:  PL/pgSQL function porcentaje_ocupation(date,date) line 8 at assignment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I won't suggest that we should create a different way of handling this exceptions, but we could if we want. In the next part we will handle exceptions and return a JSON with the data and metadata we want to serve from the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking it Up a Notch: Enhancing Function Capabilities with Customized Responses and Exception Management
&lt;/h2&gt;

&lt;p&gt;And now is time to present you some other features we can use in Postgresql, like a try/catch block inside postgresql and some system data we can get inside the functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;porcentage_ocupation2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fecha_i&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fecha_f&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="c1"&gt;-- returning a JSON is a great option to return customized results&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
  &lt;span class="n"&gt;total_pax&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;total_capacity&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;porcentage&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- will get the stack of errors here&lt;/span&gt;
  &lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- will build the result here&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="c1"&gt;-- begin the function&lt;/span&gt;
  &lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="c1"&gt;-- this second begin works as the try in a try/catch block&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pax&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;total_pax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_capacity&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;do_res&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="n"&gt;fecha_i&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;fecha_f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;porcentage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;total_pax&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total_capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'isError'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'result'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;porcentage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;EXCEPTION&lt;/span&gt; &lt;span class="c1"&gt;-- and here is the catch in the try/catch block&lt;/span&gt;
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;OTHERS&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
      &lt;span class="k"&gt;GET&lt;/span&gt; &lt;span class="n"&gt;STACKED&lt;/span&gt; &lt;span class="k"&gt;DIAGNOSTICS&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PG_EXCEPTION_CONTEXT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- how we can obtain the stack of the error in postgresql&lt;/span&gt;
      &lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'isError'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SQLERRM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'errorCode'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SQLSTATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'stack'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;-- SQLERRM, SQLSTATE are system variables provided by postgresql&lt;/span&gt;
  &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- end of the try/catch block&lt;/span&gt;
  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- returning the response&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with this second function created, lets see the result when we end up dividing by 0.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;porcentage_ocupation2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'2022-01-01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'2022-12-31'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                                                      &lt;span class="n"&gt;porcentage_ocupation2&lt;/span&gt;                                                
&lt;span class="c1"&gt;----------------------&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;"isError"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"message"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"division by zero"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"errorCode"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"22012"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"stack"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"PL/pgSQL function porcentage_ocupation2(date,date) line 11 at assignment"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like I said before, I won't defend if this is a good practice or not, i will just say that we could handle the exceptions ocurring inside the execution of the code. It will be the decision of the project manager or Senior developers if this is helpful or not.&lt;/p&gt;

&lt;p&gt;Let's jump into the controller in Nodejs, because we have reached the prerequisites to achive our goal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;db connected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`SELECT porcentage_ocupation2('2022-01-01','2022-12-31' as result)`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// looking for the result straight to the point it should be&lt;/span&gt;
      &lt;span class="nx"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disconnected from db&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="cm"&gt;/*
db connected
{
  isError: true,
  message: 'division by zero',
  errorCode: '22012',
  stack: 'PL/pgSQL function porcentage_ocupation2(date,date) line 11 at assignment'
}
disconnected from db
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are taking advantage of the fact that returning a JSON from the Postgres Function, will return 1 and only 1 row. That's why we can always look for the result in that place of the object. This is exactly the feature we wanted to build. Now let's move forward to implement it on CRUD operations, as it is more  meaningful than doing it in a SELECT QUERY.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning Concepts into Code: Writing INSERT and UPDATE Logic in PostgreSQL Functions
&lt;/h2&gt;

&lt;p&gt;We are about to finish so let's not waste any more time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;create_reservation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;_pax&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;_capacity&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;_do_res&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;_to_res&lt;/span&gt; &lt;span class="nb"&gt;TIME&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
  &lt;span class="n"&gt;inserted_reservation&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- here will retrived the inserted object&lt;/span&gt;
  &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;stack_info&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;BEGIN&lt;/span&gt;
    &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;_do_res&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
      &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'isError'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- It could be considered an error (a bad request error)&lt;/span&gt;
        &lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Bad request: No record inserted - You cant create a reservation in the past'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'rowsAffected'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'result'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;ELSE&lt;/span&gt;
      &lt;span class="k"&gt;BEGIN&lt;/span&gt;
        &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;pax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;do_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;to_res&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;_pax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;_capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;_do_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;_to_res&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;RETURNING&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;inserted_reservation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- retriving the insert object&lt;/span&gt;

        &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;inserted_reservation&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
          &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'isError'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'No record inserted'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'rowsAffected'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;ELSE&lt;/span&gt;
          &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'isError'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'result'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inserted_reservation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'rowsAffected'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;EXCEPTION&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;OTHERS&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
          &lt;span class="k"&gt;GET&lt;/span&gt; &lt;span class="n"&gt;STACKED&lt;/span&gt; &lt;span class="k"&gt;DIAGNOSTICS&lt;/span&gt; &lt;span class="n"&gt;stack_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PG_EXCEPTION_CONTEXT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'isError'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SQLERRM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'errorCode'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SQLSTATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'stack'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stack_info&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;create_reservation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2023-07-27'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'19:00'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bear in mind that the function is adding a "constraint" to the create_reservation, not allowing to create a reservation before the CURRENT_DATE. This is an extra control that we can add using functions.&lt;/p&gt;

&lt;p&gt;With this we have reached the end of this post.&lt;/p&gt;

&lt;p&gt;In conclusion, by harnessing the power of functions in PostgreSQL, we have successfully narrowed the gap between ORM and direct database programming. Through our exploration, we have gained a deeper understanding of how to leverage functions effectively, enabling us to customize responses, handle exceptions and adding more control in our database.&lt;/p&gt;

&lt;p&gt;While working directly with the database may require additional effort, it offers unparalleled control over the data manipulation process. Functions empower developers to have fine-grained control and leverage the full potential of PostgreSQL. &lt;/p&gt;

&lt;p&gt;The moment when PostgreSQL functions truly surpass ORMs is when the server is faced with the challenge of executing complex queries or providing business intelligence. It is in these scenarios that the real power of function programming in PostgreSQL becomes apparent. Functions offer a level of flexibility and control that goes beyond the capabilities of ORMs, allowing developers to craft intricate and optimized queries tailored to specific business requirements.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>plpgsql</category>
      <category>orm</category>
    </item>
  </channel>
</rss>
