<?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>Forem: Roqkabel</title>
    <description>The latest articles on Forem by Roqkabel (@roqkabel).</description>
    <link>https://forem.com/roqkabel</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%2F143336%2F44f1e504-c2c7-41a5-bd07-4732e42ca899.jpeg</url>
      <title>Forem: Roqkabel</title>
      <link>https://forem.com/roqkabel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/roqkabel"/>
    <language>en</language>
    <item>
      <title>How to Prototype Microservices Faster with DSL and Diagrams using Platform90</title>
      <dc:creator>Roqkabel</dc:creator>
      <pubDate>Fri, 24 Apr 2026 14:21:50 +0000</pubDate>
      <link>https://forem.com/roqkabel/how-to-prototype-microservices-faster-with-dsl-and-diagrams-using-platform90-3d59</link>
      <guid>https://forem.com/roqkabel/how-to-prototype-microservices-faster-with-dsl-and-diagrams-using-platform90-3d59</guid>
      <description>&lt;p&gt;Microservices become expensive before you even build the real product.&lt;br&gt;
You spend time setting up infrastructure, configs, and diagrams instead of solving the actual problem.&lt;br&gt;
And most architecture diagrams are outdated before the sprint even ends.&lt;/p&gt;

&lt;p&gt;This post is about collapsing that gap — using a Domain-Specific Language (DSL) and live architecture diagrams to prototype a working microservice system in minutes, not days.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbymccmfl80x5yn2z6o6v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbymccmfl80x5yn2z6o6v.png" alt="Platform90 DSL syntax" width="422" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The real cost of prototyping microservices&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A standard prototype cycle looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sketch architecture in Miro or Excalidraw&lt;/li&gt;
&lt;li&gt;Scaffold three or four service repos&lt;/li&gt;
&lt;li&gt;Set up Dockerfiles, a Compose file, and an Nginx reverse proxy&lt;/li&gt;
&lt;li&gt;Write database migrations&lt;/li&gt;
&lt;li&gt;Implement boilerplate route handlers just to test a POST endpoint&lt;/li&gt;
&lt;li&gt;Update the diagram — which is already wrong&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By the time you have something you can actually demo, half a sprint is gone. Worse, the diagram and the running system are already out of sync.&lt;/p&gt;


&lt;h2&gt;
  
  
  DSL as the single source of truth
&lt;/h2&gt;

&lt;p&gt;A Domain-Specific Language lets you express &lt;em&gt;intent&lt;/em&gt; rather than &lt;em&gt;implementation&lt;/em&gt;. Instead of writing a route handler, a migration, and a controller, you declare what a service does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Manage user profiles and creation"&lt;/span&gt;

  &lt;span class="no"&gt;GET&lt;/span&gt;    &lt;span class="s2"&gt;"/users"&lt;/span&gt;
  &lt;span class="no"&gt;POST&lt;/span&gt;   &lt;span class="s2"&gt;"/users"&lt;/span&gt;        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CreateUser&lt;/span&gt;
  &lt;span class="no"&gt;GET&lt;/span&gt;    &lt;span class="s2"&gt;"/users/:id"&lt;/span&gt;
  &lt;span class="no"&gt;DELETE&lt;/span&gt; &lt;span class="s2"&gt;"/users/:id"&lt;/span&gt;

  &lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;AuthService&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="nc"&gt;CreateUser&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="no"&gt;QUEUE&lt;/span&gt; &lt;span class="n"&gt;notify&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;welcome&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="n"&gt;string&lt;/span&gt;    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;string&lt;/span&gt;
  &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;string&lt;/span&gt;    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;
  &lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;  &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That block contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The full HTTP surface of a service&lt;/li&gt;
&lt;li&gt;Its dependency on another service&lt;/li&gt;
&lt;li&gt;A queue event it emits&lt;/li&gt;
&lt;li&gt;The data model it persists, with field types and constraints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything downstream — the database table, the route handler, the API contract — can be derived from this declaration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Diagrams that cannot drift
&lt;/h2&gt;

&lt;p&gt;One of the most overlooked challenges in microservice architecture is keeping system diagrams up to date.&lt;/p&gt;

&lt;p&gt;An Excalidraw file does not know that you renamed a service last Tuesday. A Confluence page does not know a new queue topic was added. Teams end up with diagrams that describe the system as it was, not as it is.&lt;/p&gt;

&lt;p&gt;When your diagram is generated directly from the DSL, this problem disappears. The architecture is visualised from the same source that runs the system. Adding a &lt;code&gt;depends_on&lt;/code&gt; edge in the schema immediately renders the arrow in the diagram. Removing a route removes it from the graph. The diagram is a live projection, not a separate artefact.&lt;/p&gt;

&lt;p&gt;This matters especially during prototyping, when the architecture changes many times per day. You stop context-switching between your editor and a diagramming tool. The diagram updates as you type.&lt;/p&gt;




&lt;h2&gt;
  
  
  The prototype workflow in practice
&lt;/h2&gt;

&lt;p&gt;Here is what a fast prototype cycle looks like when DSL and diagrams are unified:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Define services and dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Start with the service topology. What services exist? What do they depend on? You can write this in five minutes for most prototypes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="no"&gt;POST&lt;/span&gt; &lt;span class="s2"&gt;"/orders"&lt;/span&gt;       &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PlaceOrder&lt;/span&gt;
  &lt;span class="no"&gt;GET&lt;/span&gt;  &lt;span class="s2"&gt;"/orders/:id"&lt;/span&gt;
  &lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;InventoryService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="nc"&gt;InventoryService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="no"&gt;GET&lt;/span&gt;  &lt;span class="s2"&gt;"/inventory/:sku"&lt;/span&gt;
  &lt;span class="no"&gt;PUT&lt;/span&gt;  &lt;span class="s2"&gt;"/inventory/:sku"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="no"&gt;POST&lt;/span&gt; &lt;span class="s2"&gt;"/payments"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ChargeCard&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The diagram renders the three-service topology immediately. You can share it with a product manager or tech lead before writing any implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Add models and actions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Flush out the data model and the side effects of each action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="nc"&gt;PlaceOrder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;
  &lt;span class="no"&gt;QUEUE&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reserve&lt;/span&gt;
  &lt;span class="no"&gt;QUEUE&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;charge&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="nc"&gt;ChargeCard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="no"&gt;QUEUE&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;charge&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="n"&gt;string&lt;/span&gt;   &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
  &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;string&lt;/span&gt;
  &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;string&lt;/span&gt;
  &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;integer&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;string&lt;/span&gt;   &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The diagram now shows the queue topics flowing between services. The data model is visible as a node connected to its owning service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniod676n6x4d017hd9ag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniod676n6x4d017hd9ag.png" alt="Platform90 diagram on an ecommerce application" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 — Hit a real endpoint&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With a platform that executes the DSL, saving the schema gives you a live HTTP endpoint. You can &lt;code&gt;curl&lt;/code&gt; it, run Postman tests against it, or connect a frontend. The database tables are provisioned automatically. You have not written a single route handler.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mbnhve55bs9ldwfgdmw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mbnhve55bs9ldwfgdmw.png" alt="Platform90 endpoints" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why an all-in-one platform changes the equation
&lt;/h2&gt;

&lt;p&gt;The DSL-and-diagram approach reaches its full potential when the same schema that generates your diagram also runs your backend. That is the promise behind platforms like Platform90.&lt;/p&gt;

&lt;p&gt;Without a unified platform, you still have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build a DSL interpreter yourself&lt;/li&gt;
&lt;li&gt;Wire the interpreter output to a runtime (Express, Fastify, etc.)&lt;/li&gt;
&lt;li&gt;Provision a database and run migrations&lt;/li&gt;
&lt;li&gt;Set up an API gateway&lt;/li&gt;
&lt;li&gt;Deploy everything somewhere&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of those steps is solvable, but they are not the problem you came to solve. An all-in-one platform absorbs all of it.&lt;/p&gt;

&lt;p&gt;The engineer writes a DSL schema. The platform:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you declare&lt;/th&gt;
&lt;th&gt;What the platform does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;service Foo { GET "/items" }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Registers the route on the live gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model Item { id: string @id }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates the Postgres table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;action CreateItem { Model.save Item }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wires the route to the persistence layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;depends_on: [OtherService]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Renders the dependency edge in the diagram&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;QUEUE topic.name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Provisions the queue topic and shows it in the diagram&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The architect gets a diagram. The frontend team gets a live endpoint. Everyone is working from the same artefact.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to use this approach
&lt;/h2&gt;

&lt;p&gt;DSL-driven prototyping is most valuable when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The architecture is still being explored.&lt;/strong&gt; You want to try three different service topologies without committing to six repo scaffolds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need cross-functional alignment quickly.&lt;/strong&gt; A living diagram generated from the schema is a better alignment tool than a slide deck.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You are building an API-first product.&lt;/strong&gt; The contract exists before the implementation — the DSL is the contract.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iteration speed matters more than fine-grained control.&lt;/strong&gt; You trade some low-level customisation for a dramatic reduction in setup cost.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Slow microservice prototyping is mostly an overhead problem, not a complexity problem. The DSL-plus-diagram pattern attacks that overhead directly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One file describes the entire system&lt;/li&gt;
&lt;li&gt;The diagram is generated from the schema, so it cannot drift&lt;/li&gt;
&lt;li&gt;A platform that executes the schema means the prototype is also the running application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Engineers spend their time on the problem, not on the plumbing.&lt;/p&gt;

&lt;p&gt;The next time you build a new service setup, ask yourself what you’re really trying to understand.&lt;br&gt;
If it’s the architecture, data flow, or APIs — start with the schema first.&lt;br&gt;
Let the tooling generate the rest.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Platform90 is a DSL-first microservice platform that generates live architecture diagrams and running HTTP APIs from a single schema file. &lt;a href="https://platform90.up.railway.app" rel="noopener noreferrer"&gt;Start a free workspace →&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>diagrams</category>
      <category>programming</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
