<?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: do-md</title>
    <description>The latest articles on DEV Community by do-md (@domd_16).</description>
    <link>https://dev.to/domd_16</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%2F3677160%2Fb4fa4ac8-3454-4a54-ac59-c5f64c8b5ffc.png</url>
      <title>DEV Community: do-md</title>
      <link>https://dev.to/domd_16</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/domd_16"/>
    <language>en</language>
    <item>
      <title>The Missing “M” in React: I Built Zenith to Restore a Real Model Layer</title>
      <dc:creator>do-md</dc:creator>
      <pubDate>Wed, 24 Dec 2025 15:50:40 +0000</pubDate>
      <link>https://dev.to/domd_16/the-missing-m-in-react-i-built-zenith-to-restore-a-real-model-layer-2h35</link>
      <guid>https://dev.to/domd_16/the-missing-m-in-react-i-built-zenith-to-restore-a-real-model-layer-2h35</guid>
      <description>&lt;h2&gt;
  
  
  The Lost Model
&lt;/h2&gt;

&lt;p&gt;When we talk about React, we often repeat the mantra:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;UI = f(State)&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;React does an excellent job solving the &lt;strong&gt;View&lt;/strong&gt; layer.&lt;br&gt;
But when it comes to the &lt;strong&gt;Model&lt;/strong&gt; layer, the ecosystem has been searching for answers for years.&lt;/p&gt;

&lt;p&gt;From Redux, to Hooks, to Zustand, we’ve been moving toward &lt;em&gt;atomic&lt;/em&gt;, &lt;em&gt;fragmented&lt;/em&gt; state management.&lt;br&gt;
This brings minimal APIs and great ergonomics—but also a serious side effect:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Model itself is broken apart.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Have you seen this pattern before?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;State&lt;/strong&gt; lives in a &lt;code&gt;create()&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Computed values&lt;/strong&gt; are scattered across &lt;code&gt;useMemo&lt;/code&gt; calls or selector functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior (Actions)&lt;/strong&gt; ends up inside &lt;code&gt;useEffect&lt;/code&gt;s or event handlers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;Model disappears&lt;/strong&gt;, replaced by logic fragments spread everywhere.&lt;/p&gt;


&lt;h2&gt;
  
  
  Zenith: Rebuilding the Model Layer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/do-md/zenith" rel="noopener noreferrer"&gt;Zenith&lt;/a&gt;&lt;/strong&gt; focuses on &lt;strong&gt;high cohesion through co-location&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It brings &lt;strong&gt;State&lt;/strong&gt;, &lt;strong&gt;Computed&lt;/strong&gt;, and &lt;strong&gt;Actions&lt;/strong&gt; back together—into a single, self-consistent unit.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Zenith = Zustand’s minimalism + MobX’s organization + Immer’s immutable foundation&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Core Idea: An “Honest” Model
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. A Complete Model Definition (Co-location)
&lt;/h3&gt;

&lt;p&gt;In Zenith, you don’t “peek” at state using &lt;code&gt;get()&lt;/code&gt; inside closures, and you don’t rely on opaque &lt;code&gt;set&lt;/code&gt; logic.&lt;/p&gt;

&lt;p&gt;A Store &lt;em&gt;is&lt;/em&gt; a complete business model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoStore&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ZenithStore&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. State&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all&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="c1"&gt;// 2. Computed&lt;/span&gt;
  &lt;span class="c1"&gt;// No selectors. No useMemo.&lt;/span&gt;
  &lt;span class="c1"&gt;// Just native getters for derived state.&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;filteredTodos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// ...logic&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Actions&lt;/span&gt;
  &lt;span class="c1"&gt;// Use `this` honestly.&lt;/span&gt;
  &lt;span class="c1"&gt;// The UI must never touch state directly.&lt;/span&gt;
  &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;produce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;draft&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;draft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not about OOP for its own sake—it’s about &lt;strong&gt;restoring conceptual integrity&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Chained Derivations: Automatic Data Flow
&lt;/h3&gt;

&lt;p&gt;One of MobX’s most attractive features is its automatic reactivity.&lt;/p&gt;

&lt;p&gt;Zenith reproduces this behavior—&lt;strong&gt;but on top of immutable data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can derive one computed value from another:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A → B → C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;strong&gt;A&lt;/strong&gt; changes, &lt;strong&gt;C&lt;/strong&gt; updates automatically.&lt;/p&gt;

&lt;p&gt;No dependency arrays.&lt;br&gt;
No manual memo chains.&lt;br&gt;
No derived logic leaking into components.&lt;/p&gt;

&lt;p&gt;All computation stays &lt;strong&gt;inside the Model&lt;/strong&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  3. Components as Views — Zustand-Level Simplicity
&lt;/h3&gt;

&lt;p&gt;Strict models are useless if consuming them is painful.&lt;/p&gt;

&lt;p&gt;Zenith keeps the React side extremely simple, fully aligned with Hooks.&lt;/p&gt;

&lt;p&gt;No HOCs.&lt;br&gt;
No &lt;code&gt;connect&lt;/code&gt;.&lt;br&gt;
Just hooks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useStoreApi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createReactStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TodoStore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ✅ Select state like Zustand&lt;/span&gt;
  &lt;span class="c1"&gt;// Re-renders only when filteredTodos changes&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useStore&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filteredTodos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// ✅ Access the full Model instance (Actions)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useStoreApi&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&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;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;todo&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="c1"&gt;// UI triggers intent, not business logic&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;))}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&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;p&gt;The UI stays declarative.&lt;br&gt;
The Model stays authoritative.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Engineering-First by Design
&lt;/h3&gt;

&lt;p&gt;Zenith is not just a state library.&lt;/p&gt;

&lt;p&gt;It ships with built-in &lt;strong&gt;History (undo / redo)&lt;/strong&gt; and &lt;strong&gt;DevTools middleware&lt;/strong&gt;, designed for complex applications.&lt;/p&gt;

&lt;p&gt;I use it in &lt;strong&gt;&lt;a href="https://demo.domd.app/?src=https://github.com/do-md/zenith" rel="noopener noreferrer"&gt;domd&lt;/a&gt;&lt;/strong&gt;—a Markdown WYSIWYG editor that smoothly handles &lt;strong&gt;20,000+ lines of content&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is not theoretical architecture.&lt;br&gt;
It’s battle-tested.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Zenith is not here to argue whether FP or OOP is “better”.&lt;/p&gt;

&lt;p&gt;It simply acknowledges a reality:&lt;/p&gt;

&lt;p&gt;When your application logic grows,&lt;br&gt;
when you’re tired of jumping across dozens of Hooks and files to understand one feature—&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;you deserve a complete, honest Model layer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bring order back to your codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/do-md/zenith" rel="noopener noreferrer"&gt;https://github.com/do-md/zenith&lt;/a&gt;&lt;br&gt;
Stars ⭐ and issues are welcome.&lt;/p&gt;

</description>
      <category>react</category>
      <category>frontend</category>
      <category>redux</category>
    </item>
  </channel>
</rss>
