<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="346458" data-post-id="346458">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="LostKobrakai" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/LostKobrakai/120/3072_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  LostKobrakai
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="Sorc96" data-post="21" data-topic="67389">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sorc96/48/28361_2.png" class="avatar"> Sorc96:</div>
<blockquote>
<p>Is the schema doing anything at this point, though? Would there be any difference with a regular struct?</p>
</blockquote>
</aside>
<p>Depends on what you expect it to do. I still declares relationships e.g. to preload them, it still does all the ecto type stuff for converting between various representations of the types values, it still comes with all the reflection around schemas, …</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="346458" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/design-discussion-ecto-is-your-application/67389/22">Post #21</a>
	                </div>
	            </div>
              <div id="likers-container-346458" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="346458"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #21"></div>
  </section>
</div>
    <div class="postbit" id="346486" data-post-id="346486">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="katafrakt" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/katafrakt/120/34763_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  katafrakt
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Thanks for this post. It touches on a lot of things that have been on my mind for last few years, working on larger Phoenix applications in larger teams. Quite often it felt like following Ecto “defaults” is limiting and leads to database leaking everywhere. At the same time the community does not seem to have an alternative proposal.</p>
<p>I completely agree that “designing with persistence” is problematic. My perhaps favourite example is that we had a database table representing a Thing. Part of the data in this Thing was updated quite seldom in a long-running transactions, another part was updated frequently and pretty much atomically (think: views count on an article). The problem: when the long transaction was running, it locked the row and those “quick updates” kept piling up, ultimately timing out, flooding our error reporting and leading to data loss. The solution was to split the table into two tables - now slow transaction locked only part of data.</p>
<p>I’m sharing this example because I think it’s pretty convincing one for the fact, that storage has its quirks and ways of work that enforces some decisions there that make no sense from domain logic level. There two tables were modeling a single Thing, always queried together. But of course there were two Ecto schemas and preloading everywhere…</p>
<h2><a name="p-346486-custom-mapping-1" class="anchor" href="#p-346486-custom-mapping-1" aria-label="Heading link" rel="nofollow"></a>Custom mapping</h2>
<p>I admit that in one project after long discussions we tried this approach. But it did not work well. Especially at the beginning it was seen as just an unnecessary boilerplate. And even worse, we decided to have <code>Schemas.Order</code> and <code>Structs.Order</code>, so every time you saw <code>Order</code> in the code, you had to check which one is aliased.</p>
<p>There was probably a lot of poor judgement and bad design involved, but this turned out to not really be beneficial.</p>
<h2><a name="p-346486-so-what-else-2" class="anchor" href="#p-346486-so-what-else-2" aria-label="Heading link" rel="nofollow"></a>So what else?</h2>
<p>Two things come to mind as alternative approaches:</p>
<ol>
<li>
<p>CQRS, not necessarily paired with event sourcing, to separate read modeling from write modeling. Unfortunately that it a hard sell for many teams, because the concept was paired with other concepts too often and people think of it as a package with Kafka, eventual consistency, poor transactionality etc.</p>
</li>
<li>
<p>Embrace repositories. Let schemas be just a thin wrapper over database tables. But all meaningful data reading and writing should go through repository modules. This leaves schemas as purely application layer. Repositories sit on the border between application and domain layer, accepting and emitting domain structs, internally working with Ecto schemas. Unfortunately you have to probably give up on Ecto changesets for that or limit their usage only for type coercing, which again makes it a hard sell. Validation in that case should probably be done with something like <a href="https://hexdocs.pm/drops/readme.html" rel="noopener nofollow ugc">Drops</a> or with schemaless changesets.</p>
</li>
</ol>
<p>The second approach is also a bit difficult to explain to people because of <code>Ecto.Repo</code> module. I’ve heard sometimes that “we already have a repository, what are you talking about?”. And on top of that, repositories are not so easy to design and I haven’t found a lot of good resources about that.</p>
<p>Anyway, many thanks for starting this discussion here. A lot of great replies already. Perhaps if we have more of this kind of conversation, we’ll come up with some ideas.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="346486" data-batch-url="/posts/batch_likers">
                        7
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/design-discussion-ecto-is-your-application/67389/23">Post #22</a>
	                </div>
	            </div>
              <div id="likers-container-346486" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="346486"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #22"></div>
  </section>
</div>
    <div class="postbit" id="346488" data-post-id="346488">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Sorc96" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Sorc96/120/28361_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Sorc96
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Thinking about this now, you’re right. Schemas still keep most of their functionality. So I guess the only issue is that a schema like this does not quite feel like a first class citizen. Instead of <code>Repo.all(Schema)</code>, the code would now need to look like <code>Repo.all(Schema.base_query())</code>, which may not actually be a real problem. As already mentioned, the insert, update and probably delete story would be more complicated anyway.</p>
<p>Thanks for your replies!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="346488" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/design-discussion-ecto-is-your-application/67389/24">Post #23</a>
	                </div>
	            </div>
              <div id="likers-container-346488" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="346488"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #23"></div>
  </section>
</div>
    <div class="postbit" id="346489" data-post-id="346489">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Sorc96" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Sorc96/120/28361_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Sorc96
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I have very similar experience. Mostly with Ruby on Rails, where Active Record really ends up dictating the design of everything, but I have noticed similar issues with Elixir and Ecto. Other frameworks in other languages seem to have the same problem.</p>
<p>I don’t have experience with CQRS, but I’m certainly interested in trying it to see how I like the result. If you know of a good learning resource for it (without the extra parts you mentioned), I’d love to take a look.</p>
<p>As for repositories, I think those would fit the custom mapping proposal, unless I’m mistaken? I don’t even necessarily mind the boilerplate (although convincing others may not be easy), but my main worry is the fact that the design of the repositories is arbitrary. They will undoubtedly end up designed inconsistently and change throughout the project’s lifetime.</p>
<p>So I’m still hopeful for better mapping capabilities as part of Ecto, which would allow us to keep the nice features like changesets and avoid custom mapping, while offering good enough freedom for domain design.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="346489" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/design-discussion-ecto-is-your-application/67389/25">Post #24</a>
	                </div>
	            </div>
              <div id="likers-container-346489" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="346489"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #24"></div>
  </section>
</div>
    <div class="postbit" id="346490" data-post-id="346490">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="LostKobrakai" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/LostKobrakai/120/3072_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  LostKobrakai
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="Sorc96" data-post="24" data-topic="67389">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sorc96/48/28361_2.png" class="avatar"> Sorc96:</div>
<blockquote>
<p>So I guess the only issue is that a schema like this does not quite feel like a first class citizen. Instead of <code>Repo.all(Schema)</code>, the code would now need to look like <code>Repo.all(Schema.base_query())</code>, which may not actually be a real problem.</p>
</blockquote>
</aside>
<p>Yeah, it really isn’t. <code>Schema</code> in that place is just one of a few <code>Ecto.Queryable</code> implementations. Under the hood that’s basically turned into a query as well. If you want to be really sneaky you could even make <code>__schema__/1</code> overridable and change the implemenation for <code>__schema__(:query)</code>.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="346490" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/design-discussion-ecto-is-your-application/67389/26">Post #25</a>
	                </div>
	            </div>
              <div id="likers-container-346490" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="346490"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #25"></div>
  </section>
</div>
    <div class="postbit" id="346491" data-post-id="346491">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="katafrakt" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/katafrakt/120/34763_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  katafrakt
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="Sorc96" data-post="25" data-topic="67389">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sorc96/48/28361_2.png" class="avatar"> Sorc96:</div>
<blockquote>
<p>As for repositories, I think those would fit the custom mapping proposal, unless I’m mistaken?</p>
</blockquote>
</aside>
<p>Yes and no. In your example and in the attempt I made before the mapping was still 1:1 between Ecto schema and a “domain struct”. While you can roll up few columns into nicer structure (as in edition in your example), you are still limited by your database design.</p>
<p>In my understanding repositories lift this limitation a bit, allowing to build domain structs from multiple schemas. Let’s say you have multilingual e-commerce.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule App.Catalog.Repository do
  def get_product(id, language) do
    case Repo.get(ProductSchema, id) do
      nil -&gt;
         nil

      product -&gt;
        description = Repo.get_by(ProductDescriptionSchema, product_id: id, language: language)
        price = Repo.one(from p in ProductPriceSchema, where: p.product_id == ^id and p.valid_since &lt; DateTime.now(), order_by: [desc: p.valid_since]))
        %App.Catalog.Product{name: product.name, price: price.value, description: description.body)
    end
  end
end
</code></pre>
<p>And in the <code>App.Orders</code> context you would probably query for the price valid at the moment when the order was placed instead of current price + you probably don’t care about the description at all. The <code>App.Orders.Product</code> struct will look a bit different.</p>
<aside class="quote no-group" data-username="Sorc96" data-post="25" data-topic="67389">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sorc96/48/28361_2.png" class="avatar"> Sorc96:</div>
<blockquote>
<p>I don’t have experience with CQRS, but I’m certainly interested in trying it to see how I like the result. If you know of a good learning resource for it (without the extra parts you mentioned), I’d love to take a look.</p>
</blockquote>
</aside>
<p>I don’t, unfortunately, especially not for Elixir (maybe something for .NET, where I think it’s most popular). Maybe I’ll draft a quick example myself in the upcoming days, if time allows.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="346491" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/design-discussion-ecto-is-your-application/67389/27">Post #26</a>
	                </div>
	            </div>
              <div id="likers-container-346491" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="346491"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #26"></div>
  </section>
</div>
    <div class="postbit" id="346495" data-post-id="346495">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="joaoevangelista" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/joaoevangelista/120/39742_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  joaoevangelista
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I’m getting back into Elixir, and after some time in the OO world, I’m shooting myself in the foot trying to model data, instead we should model functions. Pretty data won’t solve any problems. That being said, ORM is a tempting path to follow that will bite you when you try to do things differently, either by lack of support from the database or the ORM. They are good when the domain can be mapped into the database, the schema can be rendered, changed, and then saved without worrying about SQL and everything becomes easy.</p>
<p>About Ecto, the bad thing is that it is a multi-faceted library, the early split of the SQL module helped. You don’t need to use the SQL module and Repo functions if your database can’t be mapped in a way that makes sense. Ecto can sit at the edges of your application, validating incoming data, both from the web and from the database (yes the database, where there is always a null field that shouldn’t be and it wreaks the application). You can write your queries and give them to Ecto to execute/sanitize them. That is especially useful when dealing with a legacy database or a database first design, you get to load only the data you need to that given case, and you don’t subject yourself to mapping 30 joins on the schema. You can use <a href="https://hexdocs.pm/ecto/Ecto.Repo.html#c:load/2" rel="noopener nofollow ugc">Repo.load/2</a> to fill your schema for that particular query result (but I don’t think it will validate the data). You can wrap it all with a <s>DAO</s> Data Access Module, which is a repository from DDD but you don’t get into the argument “we already have a Repo”.</p>
<p>TLDR: Functions over Data. ORM sucks, write SQL</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="346495" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/design-discussion-ecto-is-your-application/67389/28">Post #27</a>
	                </div>
	            </div>
              <div id="likers-container-346495" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="346495"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #27"></div>
  </section>
</div>
    <div class="postbit" id="346547" data-post-id="346547">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Sorc96" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Sorc96/120/28361_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Sorc96
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I’m a big fan of overriding the default query, although doing it this way feels way too hacky and unwieldy. I would love this becoming a callback on a potential Ecto.Schema behaviour. Then the documentation could show that overriding the query is possible and even expected in non-trivial situations.</p>
<p>You have shown that it is easy to use shcemas with custom queries already, but I think this could be the small step that makes the feature really feel intentional. Especially for refactoring the database design, the application code would only need to change in this one place.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="346547" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/design-discussion-ecto-is-your-application/67389/29">Post #28</a>
	                </div>
	            </div>
              <div id="likers-container-346547" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="346547"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #28"></div>
  </section>
</div>
    <div class="postbit" id="346606" data-post-id="346606">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="lithium" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/lithium/120/36633_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  lithium
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group">
<blockquote>
<p>Improving the database design would involve normalizing the data and splitting it into multiple tables. Just imagine the nightmare of all those JOINs and Ecto.Schema associations we would need in our application. That is clearly not worth the extra complexity.</p>
</blockquote>
</aside>
<p>I kind of disagree here, some JOINs don’t bother me that much. If they do, you can hide them behind a database view (which still makes inserts slightly more annoying and breaks the “code first” strategy somewhat) and/or have your database mapping layer return a neat denormalized list of maps/tuples/your-preference (like you did later in your post) and the rest of your application can happily ignore the relational details. In my experience unless your domain is changing constantly (which I have experienced, total nightmare) the data mapping/whatever layer won’t need to change <em>that</em> much if you’ve done a good job modelling your problem in the database.</p>
<aside class="quote no-group">
<blockquote>
<p>Let’s face it, this design with a bunch of nullable columns is both terrible application design and suboptimal database design.</p>
</blockquote>
</aside>
<p>100% agree with you there. That table design would get your pull request (gently) rejected at my job.</p>
<aside class="quote no-group">
<blockquote>
<p>Yet, it is the design I see every day in the projects I work on. I think that’s because the tools we have make it the only easy option.</p>
</blockquote>
</aside>
<p>That is painful. I agree that it’s easier to just spit out some flat denormalized tables but it’s very rarely worth it. I don’t think it’s <em>that</em> hard to do proper normalization once you’ve done it a few times, even if you give up some ORM magic. Especially with LLMs around to type out boilerplate for you. I genuinely think you’d be better off having tables with an id and json column and treating them as document stores rather than a halfway designed relational schema that will contain invalid data anyway. There’s also Ecto embedded schemas if you want to have sub-entities in your code but keep everything denormalized into a single table in the database. IIRC you can add constraints that test json values in postgres too, but I don’t remember exactly how much they can do.</p>
<aside class="quote no-group">
<blockquote>
<p>After all, what’s the point of ORMs if we need to do the mapping ourselves anyway? This</p>
</blockquote>
</aside>
<p>This is kind of why I don’t use them much on my personal projects. Or rather, I don’t use many of their complex/magical features. I view application domain objects and the database as FUNDAMENTALLY different things that will likely need manual mapping. The application domain objects exist to be used in application code, freely passed around, transformed, and deconstructed as needed. The relational database primarily (it facilitates efficient querying too of course, but so do NoSQL databases) exists to provide a repository where only correct, consistent data can live. You may be able to generate a <code>Book</code> struct without an ID, but if you try to send that data to the database it should reject it. It should also reject any entities that try to reference a book with an ID that doesn’t exist, for example, so it does more than just validate the shape of a single struct. I don’t think there can ever be a great automagical mapper between domain objects and relational table for the same reason you can’t blindly cast(not the right word but whatever) JSON from an HTTP request to domain objects: They are distinct entities that model the underlying reality in fundamentally different ways so that we can make different trade-offs.</p>
<aside class="quote no-group">
<blockquote>
<p>but in order for Ecto to be good enough as an ORM, I’m afraid it would need to evolve way beyond what it is now</p>
</blockquote>
</aside>
<p>I think Ecto is already a pretty good ORM. Granted, I’ve only really worked with .NET EF Core professionally, and don’t use many of its bells and whistles, but so far I have not missed anything while usng Ecto. I really love how you can insert SQL queries/query fragments everyhere too. My only complaint so far is that it doesn’t have out of the box 1st class support from some postgres features that I like, like uuidv7s or (IIRC) window functions, but that’s very minor and I understand why the team chose to make Ecto generic rather than a dedicated postgres API. Also obligatory shout-out to the Ecto developers for doing wonderful work with only a fraction of the resources that entities like Microsoft have.</p>
<p>Overall I do not think there is a clean, easy solution to the graph/relational mismatch (I like your rephrasing a lot, I will steal it if you don’t mind). Although sometimes I wonder what we could achieve by embedding postgres or SQLite directly into the runtime like Mnesia…</p>
<p>And please forgive me for anything I’ve said that is myopic or ignorant: I’ve written more SQL than anything else at this point (so I’m biased towards it) and I’ve only been a developer for 5ish years. I am also a C#/SQL Server developer at my dayjob and still learning the Elixir ecosystem.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="346606" data-batch-url="/posts/batch_likers">
                        5
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/design-discussion-ecto-is-your-application/67389/30">Post #29</a>
	                </div>
	            </div>
              <div id="likers-container-346606" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="346606"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #29"></div>
  </section>
</div>
    <div class="postbit" id="346607" data-post-id="346607">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="lithium" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/lithium/120/36633_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  lithium
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group">
<blockquote>
<p>Embrace repositories. Let schemas be just a thin wrapper over database tables. But all meaningful data reading and writing should go through repository modules. This leaves schemas as purely application layer. Repositories sit on the border between application and domain layer, accepting and emitting domain structs, internally working with Ecto schemas</p>
</blockquote>
</aside>
<p>This is what I heavily prefer. The database is not part of your application, it is an external system that you’re probably talking to over HTTP. Letting the database leak into your application will inevitably cause problems as it grows.*</p>
<p>*For those rare systems that don’t change much after the first-pass implementation, like a hacky proof of concept that miraculously manages to stay out of production, it doesn’t matter as much.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="346607" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/design-discussion-ecto-is-your-application/67389/31">Post #30</a>
	                </div>
	            </div>
              <div id="likers-container-346607" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="346607"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #30"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/67389/load_more?page=4">Load more posts (8 remaining)</a>
</div></template></turbo-stream>