<?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: Antonio Towami</title>
    <description>The latest articles on DEV Community by Antonio Towami (@towami).</description>
    <link>https://dev.to/towami</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4052077%2Fc7f9a462-5a7d-4387-b4ab-9ae5580c1e11.png</url>
      <title>DEV Community: Antonio Towami</title>
      <link>https://dev.to/towami</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/towami"/>
    <language>en</language>
    <item>
      <title>How We Run 4 Services From One Go Monorepo</title>
      <dc:creator>Antonio Towami</dc:creator>
      <pubDate>Thu, 24 Sep 2026 21:21:09 +0000</pubDate>
      <link>https://dev.to/towami/how-we-run-4-services-from-one-go-monorepo-5dlm</link>
      <guid>https://dev.to/towami/how-we-run-4-services-from-one-go-monorepo-5dlm</guid>
      <description>&lt;p&gt;We're building Towami — a commerce platform that combines white-label online stores with country marketplaces. One codebase produces four long-running services, each with a separate entry point and responsibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Four Binaries
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cmd/
├── site/     → marketplace portal + country catalogs
├── shops/    → merchant storefronts on custom domains
├── admin/    → back-office panel
└── worker/   → background jobs via Asynq (no HTTP)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;site&lt;/code&gt; serves the main domain and multiple country subdomains. &lt;code&gt;shops&lt;/code&gt; serves merchant storefronts on platform subdomains and custom domains. &lt;code&gt;admin&lt;/code&gt; is the back-office dashboard. &lt;code&gt;worker&lt;/code&gt; processes email, Telegram, and import tasks through Redis queues.&lt;/p&gt;

&lt;p&gt;The repository also has an importer command. It is a CLI utility rather than a long-running service, so it is outside the four in the title.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shared &lt;code&gt;internal/&lt;/code&gt; With Clean Boundaries
&lt;/h2&gt;

&lt;p&gt;All four binaries import from a single &lt;code&gt;internal/&lt;/code&gt; package tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;router → middleware → handler → service → repository → model/DB
                        ↘ templates (Templ)
                        ↘ dto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key is sub-packaging by application: &lt;code&gt;handler/site/&lt;/code&gt;, &lt;code&gt;handler/shops/&lt;/code&gt;, and &lt;code&gt;handler/admin/&lt;/code&gt;, with the same split for services, repositories, DTOs, and templates. Each binary imports only the packages it needs. The worker, for example, does not import HTTP handlers or templates.&lt;/p&gt;

&lt;p&gt;Go's &lt;code&gt;internal&lt;/code&gt; directory also prevents code outside the parent module tree from importing these packages. It does not enforce our layer order, though; that remains an architectural convention backed by review and tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Microservices?
&lt;/h2&gt;

&lt;p&gt;Early on, we considered splitting into separate repos. Three things stopped us:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared models.&lt;/strong&gt; Products, shops, and users are the same entities everywhere. Duplicating model definitions across repos means sync hell.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Atomic refactors.&lt;/strong&gt; When we change a database column, the handler, service, repository, DTO, and template all update in one commit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment simplicity.&lt;/strong&gt; Four builds with &lt;code&gt;CGO_ENABLED=0&lt;/code&gt; produce self-contained binaries that can be managed as independent service units. We do not need container orchestration, a service mesh, or versioned APIs between these cooperating applications.&lt;/p&gt;

&lt;p&gt;This is still a modular monolith: the binaries share code and data contracts. Separate executables alone do not make them microservices.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Gotcha: Air and Hot Reload
&lt;/h2&gt;

&lt;p&gt;In development, each service runs in Docker with &lt;a href="https://github.com/air-verse/air" rel="noopener noreferrer"&gt;Air&lt;/a&gt; for hot reload. Without scoped watch rules, an edit to shared code can rebuild every service.&lt;/p&gt;

&lt;p&gt;We solved this with scoped watch configs. The &lt;code&gt;worker&lt;/code&gt; Air config ignores &lt;code&gt;templates/&lt;/code&gt;, &lt;code&gt;handler/&lt;/code&gt;, and &lt;code&gt;router/&lt;/code&gt;. The &lt;code&gt;admin&lt;/code&gt; config ignores &lt;code&gt;handler/site/&lt;/code&gt; and &lt;code&gt;handler/shops/&lt;/code&gt;. Each service only rebuilds when files relevant to it change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# Worker: exclude HTTP and UI layers&lt;/span&gt;
&lt;span class="nn"&gt;[build]&lt;/span&gt;
  &lt;span class="py"&gt;exclude_dir&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;"internal/templates"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"internal/handler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"internal/router"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"assets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"storage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bottom Line
&lt;/h2&gt;

&lt;p&gt;A Go monorepo with one &lt;code&gt;cmd/&lt;/code&gt; entry point per service gives us process-level separation while keeping atomic refactors and one &lt;code&gt;go.mod&lt;/code&gt;. It is a good fit while the applications share a schema and are maintained by one team. If independent ownership or deployment becomes the real constraint, the boundary can change later.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Next up: how one of those binaries serves multiple country sites on different subdomains with a single process.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article is based on lessons from building &lt;a href="https://towami.com" rel="noopener noreferrer"&gt;Towami&lt;/a&gt;, a multi-country marketplace and white-label storefront platform. Follow for more practical notes on Go, HTMX, infrastructure, and SaaS.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>architecture</category>
      <category>webdev</category>
      <category>saas</category>
    </item>
  </channel>
</rss>
