{
  "path": "/3ma4mvv3lsk24",
  "site": "at://did:plc:dgtaz4vldacvqhvvmdvoc4ad/site.standard.publication/3m7iwa2i6us22",
  "tags": [
    "jobtrail",
    "devlog"
  ],
  "$type": "site.standard.document",
  "title": "Jobtrail EP 3: Laravel New'd",
  "content": {
    "$type": "pub.leaflet.content",
    "pages": [
      {
        "id": "019b2789-c1d5-7338-8a43-92acd3a49b06",
        "$type": "pub.leaflet.pages.linearDocument",
        "blocks": [
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 37,
                    "byteStart": 22
                  },
                  "features": [
                    {
                      "uri": "https://mlogs.leaflet.pub/3m7nirslulc25",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "So I mentioned in the previous devlog that I would make a detailed architecture post; well that just got a whole lot simpler!"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "facets": [],
              "plaintext": "The initial architecture"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 57,
                    "byteStart": 43
                  },
                  "features": [
                    {
                      "uri": "https://www.better-t-stack.dev/",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "The initial architecture was created using better-t-stack which is an amazing tool to kickstart a js monorepo. The problem with this approach is that you now have a bunch of dependencies that need to be bundled correctly (which better-t-stack already sets up) and - in my usecase - containerized to have the smallest image size."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 353,
                    "byteStart": 345
                  },
                  "features": [
                    {
                      "uri": "https://adonisjs.com/",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "Then came the biggest issues: mails & scheduled jobs. Don't get me wrong, there are solutions for both in Javascript ecosystem (React Email, bullmq, pgboss and more come to mind) but I felt it would add another layer of complexity the app could probably do without. So the question now became; should I just move the api layer to something like AdonisJS to have a more batteries included setup and where I could easily"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "facets": [],
              "plaintext": "The new architecture"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 104,
                    "byteStart": 97
                  },
                  "features": [
                    {
                      "uri": "https://laravel.com",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 178,
                    "byteStart": 171
                  },
                  "features": [
                    {
                      "uri": "https://inertiajs.com/",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 376,
                    "byteStart": 357
                  },
                  "features": [
                    {
                      "uri": "https://spatie.be/docs/laravel-data/v4/introduction",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "Turns out I know of a framework that has scheduling, mails, queued jobs and more out of the box: Laravel. And I can kind of keep most of my frontend code by using it with Inertia. It's not a 1:1 port by any means, though. I lose out on better-auth and orpc, having to rely on Laravel's auth system and on making the calls typesafe in another way - by using spatie/laravel-data to generate types from my DTOs."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "facets": [],
              "plaintext": "The new deployment strategy"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [],
              "plaintext": "Before, I had multiple docker images that I needed to maintain:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [],
                    "plaintext": "a helper that ran the migrations if/when there was something new"
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [],
                    "plaintext": "the actual server image"
                  },
                  "children": []
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [],
              "plaintext": "Each of these images needed to have different packages copied inside them for the build step and it was kind of messy - though still manageable."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [],
              "plaintext": "Now this is not yet finalized, but I think I might be able to either have:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [],
                    "plaintext": "a single image with different start commands in a docker-compose stack"
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 79,
                          "byteStart": 68
                        },
                        "features": [
                          {
                            "uri": "https://github.com/just-containers/s6-overlay/tree/master#writing-a-service-script",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "a single image and container with more services defined inside some s6 services"
                  },
                  "children": []
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [],
              "plaintext": "Anyway I'll keep you updated when all of this is finished :)"
            }
          }
        ]
      }
    ]
  },
  "bskyPostRef": {
    "cid": "bafyreic66ly6kjdgabvhmlfc2gtswb4j6tivrelkewaq7jsh7s5tnevbne",
    "uri": "at://did:plc:dgtaz4vldacvqhvvmdvoc4ad/app.bsky.feed.post/3ma4mvzpvvk22",
    "commit": {
      "cid": "bafyreiescyrydrpa5yvriu7sqr6kry23zggurybuk6wamuunapktprr57q",
      "rev": "3ma4mvzskzz2q"
    },
    "validationStatus": "valid"
  },
  "description": "",
  "publishedAt": "2025-12-16T17:04:15.370Z"
}