<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Posts on James Beith</title>
    <link>https://www.jamesbeith.co.uk/blog/</link>
    <description>Recent content in Posts on James Beith</description>
    <generator>Hugo -- 0.155.2</generator>
    <language>en-gb</language>
    <lastBuildDate>Mon, 25 Jun 2018 21:00:00 +1000</lastBuildDate>
    <atom:link href="https://www.jamesbeith.co.uk/blog/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How to structure Django projects</title>
      <link>https://www.jamesbeith.co.uk/blog/how-to-structure-django-projects/</link>
      <pubDate>Mon, 25 Jun 2018 21:00:00 +1000</pubDate>
      <guid>https://www.jamesbeith.co.uk/blog/how-to-structure-django-projects/</guid>
      <description>&lt;p&gt;Here are some notes on how to structure a Django project. It breaks away from structuring a project around Django “apps” and instead uses a clear separation between three core layers (data, domain, and interfaces). Let’s use the following example, an e-commerce site called “Crema” where people can purchase coffee goods. Below is a layout of the fundamental directories.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;src/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  crema/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    data/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      migrations/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      models/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    domain/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      baskets/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      orders/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      products/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      users/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    interfaces/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      actions/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        management/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          commands/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      dashboard/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        orders/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        products/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        users/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      store/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        account/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        basket/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        checkout/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        products/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  tests/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    functional/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    unit/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;src/&lt;/code&gt; directory lives in the root of the repository, alongside files such as &lt;code&gt;README.md&lt;/code&gt;. The &lt;code&gt;crema/&lt;/code&gt; directory contains all the project files, and all the test files live in the &lt;code&gt;tests/&lt;/code&gt; directory. Let’s drill down into the three main layers.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Here are some notes on how to structure a Django project. It breaks away from structuring a project around Django “apps” and instead uses a clear separation between three core layers (data, domain, and interfaces). Let’s use the following example, an e-commerce site called “Crema” where people can purchase coffee goods. Below is a layout of the fundamental directories.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-plaintext" data-lang="plaintext"><span style="display:flex;"><span>src/
</span></span><span style="display:flex;"><span>  crema/
</span></span><span style="display:flex;"><span>    data/
</span></span><span style="display:flex;"><span>      migrations/
</span></span><span style="display:flex;"><span>      models/
</span></span><span style="display:flex;"><span>    domain/
</span></span><span style="display:flex;"><span>      baskets/
</span></span><span style="display:flex;"><span>      orders/
</span></span><span style="display:flex;"><span>      products/
</span></span><span style="display:flex;"><span>      users/
</span></span><span style="display:flex;"><span>    interfaces/
</span></span><span style="display:flex;"><span>      actions/
</span></span><span style="display:flex;"><span>        management/
</span></span><span style="display:flex;"><span>          commands/
</span></span><span style="display:flex;"><span>      dashboard/
</span></span><span style="display:flex;"><span>        orders/
</span></span><span style="display:flex;"><span>        products/
</span></span><span style="display:flex;"><span>        users/
</span></span><span style="display:flex;"><span>      store/
</span></span><span style="display:flex;"><span>        account/
</span></span><span style="display:flex;"><span>        basket/
</span></span><span style="display:flex;"><span>        checkout/
</span></span><span style="display:flex;"><span>        products/
</span></span><span style="display:flex;"><span>  tests/
</span></span><span style="display:flex;"><span>    functional/
</span></span><span style="display:flex;"><span>    unit/
</span></span></code></pre></div><p>The <code>src/</code> directory lives in the root of the repository, alongside files such as <code>README.md</code>. The <code>crema/</code> directory contains all the project files, and all the test files live in the <code>tests/</code> directory. Let’s drill down into the three main layers.</p>
<h2 id="data">Data</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-plaintext" data-lang="plaintext"><span style="display:flex;"><span>data/
</span></span><span style="display:flex;"><span>  migrations/
</span></span><span style="display:flex;"><span>    __init__.py
</span></span><span style="display:flex;"><span>    0001_initial.py
</span></span><span style="display:flex;"><span>  models/
</span></span><span style="display:flex;"><span>    __init__.py
</span></span><span style="display:flex;"><span>    basket.py
</span></span><span style="display:flex;"><span>    order.py
</span></span><span style="display:flex;"><span>    product.py
</span></span><span style="display:flex;"><span>    user.py
</span></span><span style="display:flex;"><span>  __init__.py
</span></span></code></pre></div><p>The <code>data/</code> directory contains all the model and migration files for the project. Opt for one model class per module and make sure that they’re all imported into <code>models/__init__.py</code> so Django can discover them and generate migrations. This also results in one simple and consistent import of the data layer throughout the project to access the models. For example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">&gt;&gt;&gt;</span> <span style="color:#f92672">from</span> crema.data <span style="color:#f92672">import</span> models
</span></span><span style="display:flex;"><span><span style="color:#f92672">&gt;&gt;&gt;</span> products <span style="color:#f92672">=</span> models<span style="color:#f92672">.</span>Product<span style="color:#f92672">.</span>objects<span style="color:#f92672">.</span>all()
</span></span></code></pre></div><p>These model modules should only import other model modules, and possibly some generic <code>utils</code> modules. The model classes should be lightweight. Their purpose is to perform trivial read and write operations with the database. Business logic should most certainly not live in the data layer, that belongs in domain.</p>
<h2 id="domain">Domain</h2>
<p>The <code>domain/</code> directory contains all the business logic files. Each domain area is a directory containing modules of related functionality. Orders, for example, might contain the following modules. An <code>engine.py</code> module with functions related to creating new orders, an <code>operations.py</code> module for general CRUD operations, and a <code>processing.py</code> module for performing the steps related to dispatching orders.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-plaintext" data-lang="plaintext"><span style="display:flex;"><span>domain/
</span></span><span style="display:flex;"><span>  orders/
</span></span><span style="display:flex;"><span>    __init__.py
</span></span><span style="display:flex;"><span>    engine.py
</span></span><span style="display:flex;"><span>    operations.py
</span></span><span style="display:flex;"><span>    processing.py
</span></span><span style="display:flex;"><span>  __init__.py
</span></span></code></pre></div><p>The key point is that the functions within these modules remain focused on their specific domain and call upon other domain areas for theirs. With this focus, all changes flow through dedicated functions and it becomes easier to add additional operations to them as business needs change. This produces consistent results regardless of which interface is calling into the domain layer.</p>
<h2 id="interfaces">Interfaces</h2>
<p>The purpose of the interfaces layer is to validate input, call the domain layer, and return output. Similar to the data layer, business logic shouldn’t live in these interfaces, their purpose is to handle I/O.</p>
<p>There’s two HTTP interfaces in this Crema project. One named <code>store/</code> that’s public facing, and one named <code>dashboard/</code> that’s for company staff (hosted under a separate subdomain). In both interfaces Django forms, or similar, validate submitted data. A view then calls the domain layer with this validated data and converts the return value (or exception) into the appropriate HTTP response.</p>
<p>A third interface, named <code>actions/</code>, is a collection of Django management commands. Similar to the other two interfaces, a <code>BaseCommand</code> subclass would use a parser to handle the input, then make a call to the domain layer, and then write some output to <code>self.stdout</code>.</p>
<p>These interfaces will import the data layer to retrieve objects from the database. They must not, however, bypass the domain layer and perform any create, update, or delete operations. This prohibits using classes such as <code>ModelForm</code>, <code>CreateView</code>, <code>UpdateView</code>, or <code>DeleteView</code>. Whilst these Django classes can be convenient, they perform operations on models directly instead of calling upon the domain layer.</p>
<p>More interfaces might come and go, but they all follow a similar pattern of calling the domain layer to perform operations that handle the business logic.</p>
<h2 id="tests">Tests</h2>
<p>The <code>tests/</code> directory lives alongside the <code>src/</code> directory. Betters to have all the test files contained in one place rather than sprinkled amongst the project files. Group the tests into unit tests and functional tests. You may want to separate integration tests into their own group too. Structure the test modules within each of these groups into directories that mimic the project layout.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-plaintext" data-lang="plaintext"><span style="display:flex;"><span>src/
</span></span><span style="display:flex;"><span>  crema/
</span></span><span style="display:flex;"><span>    data/
</span></span><span style="display:flex;"><span>      models/
</span></span><span style="display:flex;"><span>        order.py
</span></span><span style="display:flex;"><span>  tests/
</span></span><span style="display:flex;"><span>    unit/
</span></span><span style="display:flex;"><span>      data/
</span></span><span style="display:flex;"><span>        models/
</span></span><span style="display:flex;"><span>          test_order.py
</span></span></code></pre></div><h2 id="django-apps">Django apps</h2>
<p>One last note to add is that this project structure results in fewer <code>INSTALLED_APPS</code> entries. You must still include the interfaces directories, containing templates, static files, management commands, etc., and the <code>data/</code> directory, containing the models. However, you don’t include the <code>domain/</code> directory as it doesn’t contain any of these things. Instead, it’s a collection of Python modules that Django doesn’t need to discover or search.</p>
<p>In conclusion, this project structure aims at creating a single data layer, a consistent domain layer to handle business logic, and an interfaces layer that handles different inputs and outputs.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
