<?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: Callum Lamont</title>
    <description>The latest articles on DEV Community by Callum Lamont (@calamont).</description>
    <link>https://dev.to/calamont</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%2F2619178%2F536c0db3-c7ae-4b39-b773-cb74c4e14635.jpg</url>
      <title>DEV Community: Callum Lamont</title>
      <link>https://dev.to/calamont</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/calamont"/>
    <language>en</language>
    <item>
      <title>An Initiation to Domain-Driven Design</title>
      <dc:creator>Callum Lamont</dc:creator>
      <pubDate>Mon, 13 Jan 2025 04:19:34 +0000</pubDate>
      <link>https://dev.to/calamont/an-initiation-to-domain-driven-design-223i</link>
      <guid>https://dev.to/calamont/an-initiation-to-domain-driven-design-223i</guid>
      <description>&lt;p&gt;What is Domain-Driven Design (DDD)? I used to group it with the other programming acronyms (TDD, SOLID, KISS, YAGNI, DRY, etc.). Maybe something developers &lt;em&gt;should&lt;/em&gt; practice, but often don’t. That changed after I read Vaughn Vernon's book &lt;a href="https://www.oreilly.com/library/view/implementing-domain-driven-design/9780133039900/" rel="noopener noreferrer"&gt;&lt;strong&gt;Implementing Domain Driven Design&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As developers, we are told what constitutes "good code". There should be high cohesion, low coupling, single responsibilities, information hiding, and so on. But these ideas are abstract. While the benefits of these characteristics are clear, it’s not always obvious how to achieve them. Well, DDD is one way. It describes a number of patterns that will help you write clearer, less coupled, extensible, cohesive code that aligns strongly with the business' goals.&lt;/p&gt;

&lt;p&gt;A core tenet is that your code should model the domain you are building a solution for. If a business process change is conceptually simple, so is the required code change. No more explaining to a product manager why a basic feature request will actually take weeks to implement. I am relatively new to this methodology. So I won't provide a more detailed description than that. Instead, I will share some useful DDD patterns I've come across. These are good general programming practices. You don't need to "do" DDD to follow these. But if you think they are insightful, I would encourage you to check out the resources listed at the end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some useful DDD patterns:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Understand what you're building, and why.&lt;/li&gt;
&lt;li&gt;Understand your relationship with other services.&lt;/li&gt;
&lt;li&gt;Understand the type of object you’re creating.&lt;/li&gt;
&lt;li&gt;
Use immutable Value Objects.
&lt;/li&gt;
&lt;li&gt;Use rich domain models.&lt;/li&gt;
&lt;li&gt;Hide your domain models from the outside world.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Understand what you’re building, and why
&lt;/h2&gt;

&lt;p&gt;Don’t make assumptions about what you’re building. Discuss and explore it with the domain expert. This could be your product manager or clients. Through this collaboration, both the technical and non-technical team members will better understand their business, user needs, and value they are delivering. The process of exploration may even help the domain experts uncover areas of uncertainty. Plan and design. Do not immediately jump into coding.&lt;/p&gt;

&lt;p&gt;Part of this process involves the creation of a &lt;strong&gt;Ubiquitous Language&lt;/strong&gt;, a shared language describing the domain. A common approach for this modelling process is &lt;a href="https://www.eventstorming.com/" rel="noopener noreferrer"&gt;Event Storming&lt;/a&gt;. The &lt;strong&gt;Ubiquitous Language&lt;/strong&gt; is used by both domain experts and within the codebase. As Vaughn Vernon writes in &lt;strong&gt;Implementing Domain-Driven Design&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We should build software that is as close as possible to what the business leaders and experts would create if they were the coders… There are zero translations between the domain experts, the software developers, and the software.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Imagine an application for booking tickets at a cinema. What's a good name for a function that saves the finalised booking in the backend? Should the method be &lt;code&gt;createBooking&lt;/code&gt; or &lt;code&gt;confirmBooking&lt;/code&gt;? The word "create" leaks developer knowledge into the codebase. They know a new record is being created in the database. And, ultimately, that is what this function does. But the name &lt;code&gt;confirmBooking&lt;/code&gt; better describes the real life use case. "Create" is not the verb a customer would associate with the confirmation step of the booking process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand your relationship with other services
&lt;/h2&gt;

&lt;p&gt;Before designing a service, take a moment to understand how it fits into your wider technology ecosystem. Some services exist to support unrelated parts of the business. And there are services that your new service will need to interact with. Understanding this space is called &lt;strong&gt;Context Mapping&lt;/strong&gt;. You might incidentally do this when designing a new system. However, for DDD it is an &lt;em&gt;explicit&lt;/em&gt; process. It goes so far as to characterise the relationships underpinning these services. Below are a few examples. Check out more at &lt;a href="https://github.com/ddd-crew/context-mapping" rel="noopener noreferrer"&gt;this useful repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Partnership
&lt;/h3&gt;

&lt;p&gt;The upstream and downstream teams succeed or fail together. Models and interfaces are developed to suit both their needs and any features and planned between them to minimise harm.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer-Supplier Development
&lt;/h3&gt;

&lt;p&gt;The success of the upstream team (the supplier) is independent of the downstream team. But they take the downstream's needs into account when planning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conformist
&lt;/h3&gt;

&lt;p&gt;The upstream team does not accommodate the downstream team’s needs. The downstream team conforms to the models of the upstream team, whatever they are or change to.&lt;/p&gt;

&lt;p&gt;A lot of confusion and frustration might be avoided if these relationships are agreed between participating teams ahead of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand the type of object you’re creating
&lt;/h2&gt;

&lt;p&gt;In object-oriented programming (OOP), our codebases are full of classes. These are often loosely categorised into vague concepts like services and repositories. However, these categories and their definitions vary from one project (or developer) to the next. So in each new codebase, we spend time understanding the responsibilities of each class.&lt;/p&gt;

&lt;p&gt;Without formal definitions, it is easy for the scope of these classes to grow. And then the level of abstraction becomes lost. Fortunately, DDD provides concrete definitions for model categories, and rules for how they can interact. The vocabulary is simple, yet empowering. When you don’t have to define the building blocks of your application, you can focus more on what you’re building. These are the “tactical patterns” of DDD. Some examples are:&lt;/p&gt;

&lt;h3&gt;
  
  
  Value Objects
&lt;/h3&gt;

&lt;p&gt;Immutable objects that represent a value with no identity. These can be replaced, but cannot change, over time. Two &lt;strong&gt;Value Objects&lt;/strong&gt; are considered equal if they hold the same values (i.e. structural equality).&lt;/p&gt;

&lt;h3&gt;
  
  
  Entities
&lt;/h3&gt;

&lt;p&gt;Objects that have a unique identity. Two &lt;strong&gt;Entities&lt;/strong&gt; with the same identifier are considered equal, even if all other properties are different in value. These represent concepts in your domain that you need to observe and track changes to over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aggregates
&lt;/h3&gt;

&lt;p&gt;These are a grouping of &lt;strong&gt;Entities&lt;/strong&gt; and &lt;strong&gt;Value Objects&lt;/strong&gt; that represent a consistency boundary. If there is some invariant or business rule that must be maintained when updating a group of objects (and rolled back everywhere upon failure), then they belong together in an &lt;strong&gt;Aggregate&lt;/strong&gt;. One &lt;strong&gt;Entity&lt;/strong&gt; will serve as the &lt;strong&gt;Aggregate Root&lt;/strong&gt;, through which all behaviour is orchestrated.&lt;/p&gt;

&lt;p&gt;What if another &lt;strong&gt;Entity&lt;/strong&gt; is changed at the same time as the &lt;strong&gt;Aggregate&lt;/strong&gt;? If there is no requirement to maintain consistency between the two objects (and &lt;em&gt;really&lt;/em&gt; challenge yourself on this point!) then it shouldn't be part of the &lt;strong&gt;Aggregate&lt;/strong&gt;. Instead, it can be updated through an event-driven process that is eventually consistent. The goal is to make each &lt;strong&gt;Aggregate&lt;/strong&gt; as lightweight as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Domain Services
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Domain Service&lt;/strong&gt; helps realise business logic and coordinates actions between your domain models (i.e. &lt;strong&gt;Aggregates&lt;/strong&gt;). Importantly, only use &lt;strong&gt;Domain Services&lt;/strong&gt; when the behaviour being orchestrated cannot logically belong to a single &lt;strong&gt;Aggregate&lt;/strong&gt; (see below).&lt;/p&gt;

&lt;h3&gt;
  
  
  Application Services
&lt;/h3&gt;

&lt;p&gt;These allow your domain logic to interface with the external world (API, persistence stores, etc). No business logic belongs here. Each method represents a single business use case. And each method achieves its goal by interacting with the &lt;strong&gt;Aggregates&lt;/strong&gt; and &lt;strong&gt;Domain Services&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use immutable Value Objects
&lt;/h2&gt;

&lt;p&gt;Try using &lt;strong&gt;Value Objects&lt;/strong&gt; wherever possible. Replacing both primitives and classes with &lt;strong&gt;Value Objects&lt;/strong&gt; will make your code more self-documenting, easier to maintain, and less prone to bugs.&lt;/p&gt;

&lt;p&gt;Let us consider a Kotlin application for booking tickets at a cinema. Perhaps it was first written like below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TicketDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;ticketType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TicketType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;filmId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;sessionTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;seatTheatre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;seatRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Char&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;seatNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;confirmBooking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cinemaId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ticketDetails&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TicketDetail&lt;/span&gt;&lt;span class="p"&gt;&amp;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;// Business logic...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can improve this by introducing &lt;strong&gt;Value Objects&lt;/strong&gt;. &lt;strong&gt;Value Objects&lt;/strong&gt; are immutable, so we replace all use of &lt;code&gt;var&lt;/code&gt; with &lt;code&gt;val&lt;/code&gt;. And we use &lt;code&gt;data class&lt;/code&gt; to enable structural equality when comparing objects. The same can be achieved by record classes in Java and data classes in Python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;CustomerIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;CinemaIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;FilmIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Seat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;theatre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Char&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;TicketPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&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="c1"&gt;// Validates that discount &amp;lt; price&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;TicketDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;ticketType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TicketType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;seat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Seat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TicketPrice&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;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;BookingDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CustomerIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;cinemaId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CinemaIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;filmId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FilmIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sessionTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;ticketDetails&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TicketDetail&lt;/span&gt;&lt;span class="p"&gt;&amp;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;// Validates at least one seat being booked&lt;/span&gt;
    &lt;span class="c1"&gt;// Validates all seats positions are unique&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;confirmBooking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BookingDetails&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="c1"&gt;// Business logic...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These changes introduce a number of benefits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improved readability&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Each type actually means something in our domain. They are not primitives. Also, related concepts can be grouped together. For example, all aspects of the seating location (theatre, row, seat number) are grouped into the &lt;code&gt;Seat&lt;/code&gt; class. This improves readability and reduces the number of method parameters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better type safety&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Now it’s harder to accidentally swap the order of the method parameters that previously had the same type (i.e. &lt;code&gt;UUID&lt;/code&gt;). For static languages, the compiler will complain if the values for &lt;code&gt;customerId&lt;/code&gt; and &lt;code&gt;cinemeaId&lt;/code&gt; are swapped. For dynamic programming languages, such errors can now be caught by the linter (if you use one).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improved evolvability.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
What if we want to change the data type representing an identifier (e.g. change the cinema identifier to &lt;code&gt;Int&lt;/code&gt; )? Previously, we would need to update &lt;em&gt;all&lt;/em&gt; class and function definitions using that identifier. Now we only need to update a few lines in the &lt;code&gt;CinemaIdentifier&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Basic data validation is included in the &lt;strong&gt;Value Object&lt;/strong&gt; construction. This is much better than scattering around (and possibly repeating) validation in the methods the data is used. It improves reliability, because the data is &lt;em&gt;guaranteed&lt;/em&gt; to be validated. And it improves readability, as the business logic can now focus on the use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Immutability&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Value Objects&lt;/strong&gt; are immutable. Once one is instantiated and validated, you know it is valid, always. Because &lt;strong&gt;Value Objects&lt;/strong&gt; are immutable, any methods are side-effect free. &lt;strong&gt;Value Objects&lt;/strong&gt; can be passed around a multi-threaded application with confidence.&lt;/p&gt;
&lt;h2&gt;
  
  
  Use rich domain models
&lt;/h2&gt;

&lt;p&gt;Don't compose your application with only weak (or anaemic) domain models. These are classes that hold data but don't include any business logic. The result is that all logic is pushed into what developers often label "services" (but these are different to the DDD definition of Services provided above).&lt;/p&gt;

&lt;p&gt;For an application with anaemic domain models, a developer might write a class like below to perform the business logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookingService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;confirmBooking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BookingDetails&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Business logic...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;updateBooking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bookingId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BookingIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updatedDetails&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BookingDetails&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Business logic...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;cancelBooking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bookingId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BookingIdentifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Business logic...&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;What's wrong with this? Well, the term "booking service" is pretty vague. It currently handles the logic for the creation, modification, and cancellation of a single booking. But should it include logic for listing historical bookings made by a customer? Or searching for a particular booking? Hmm, maybe, but possibly not... Regardless, if a product manager requests this functionality, I bet the logic will be added to this class. And through this process the class grows in scope and loses cohesion. And each new functionality will require more injected dependencies. Over time this scope creep creates &lt;a href="https://en.wikipedia.org/wiki/God_object" rel="noopener noreferrer"&gt;God Classes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What is the alternative? In DDD domain models aren't necessarily simple data holders. They represent a core concept in your domain. Thus, they should not only hold the data, but also the behaviours that involve that data. These are called rich domain models.&lt;/p&gt;

&lt;p&gt;In the previous section I treated the &lt;code&gt;BookingDetails&lt;/code&gt; as a &lt;strong&gt;Value Object&lt;/strong&gt;. But if we put our DDD hats on, we can see a booking is actually better modelled as an &lt;strong&gt;Entity&lt;/strong&gt;. That's because each booking is unique. It is a &lt;em&gt;thing&lt;/em&gt; that can be updated over its life. Thus we need to track it with a unique identifier. With some minor adjustments, we can improve our booking service from before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;
 &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;TicketDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;ticketType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TicketType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;seat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Seat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TicketPrice&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="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;withNewSeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newSeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Seat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TicketDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticketType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newSeat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Booking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bookingId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BookingIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BookingIdentifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CustomerIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;cinemaId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CinemaIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;filmId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FilmIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;sessionTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;ticketDetails&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TicketDetail&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;changeSeats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newSeats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Seat&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ticketDetails&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ticketDetails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newSeats&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seat&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withNewSeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seat&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="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;cancelBooking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Business logic...&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;Our booking class clearly represents a single booking, and actions associated with it. It is unlikely a future developer would bastardise this class by adding methods like &lt;code&gt;getAllBookings&lt;/code&gt; or &lt;code&gt;findBooking&lt;/code&gt;. Thus, we have better preserved the cohesion of our codebase going into the future. And maximising the maintainability of a codebase is critical for any long-lived application. There is no longer a "create" function, as this is simply performed through the act of instantiating the class. And the actions that can be made on this &lt;strong&gt;Entity&lt;/strong&gt; are clearly defined by the class methods. Instead of the vague &lt;code&gt;updateBooking&lt;/code&gt; method, we have &lt;code&gt;changeSeats&lt;/code&gt;. The code reflects the actual use case and tells the developer why it's there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hide your domain models from the outside world
&lt;/h2&gt;

&lt;p&gt;All projects have dependencies for communicating with the outside world. These could help serve RESTful endpoints, communicate with other services via RPC, or connect to a database. While necessary, none of this has anything to do with the purpose of the service. So, there is no reason to couple these external dependencies to any core business logic. This principle is often referred to as &lt;a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html" rel="noopener noreferrer"&gt;clean architecture&lt;/a&gt;. DDD is not the only proponent of this style. But, with its strong emphasis on considered domain models, it does scream out for such an approach.&lt;/p&gt;

&lt;p&gt;Any layered architecture can achieve the separation of concerns required for clean architecture. My favourite is the Ports and Adapters (i.e. Hexagonal) style. It strongly promotes the use of clean domain models. The "ports" surround your core logic and serve as points of entry to your application. These ports are typically defined as interfaces. The concrete implementations of these interfaces are the "adapters" that leverage libraries and other dependencies to serve their function.&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%2Fzutse7xumnav8up4ubz0.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%2Fzutse7xumnav8up4ubz0.png" alt="Image description" width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;
An illustration of the Ports and Adapters architecture, where arrows point in the direction of dependency. If needed, we can easily change our datastore from PostgreSQL to MongoDB. Inbound and outbound ports don't only interface with APIs and databases. This is only an example.



&lt;p&gt;&lt;br&gt;&lt;br&gt;
What's wrong with a simple three-layer architecture, with a presentation, application, and data-access layer? If done well, nothing. But, particularly if combined with an object-relational mapping (ORM) framework, it can enable developers to take shortcuts. These reduce the maintainability of the project over its life. For example, with the ORM classes it is possible for the presentation layer to directly interact with the database. And a lazy developer might choose to do this for a simple CRUD operation, instead of passing data unnecessarily through the application layer. Over time, such a shortcut might occur for another reason. And then again, and again. The result is logic spread over all the layers of your application, defeating the original purpose of the layered architecture.&lt;/p&gt;

&lt;p&gt;Further, ORMs promote database driven design. It's easier to pretend your ORM classes are the core models of your application. But that means your "model" actually reflects how the data is organised in the database, not how it's represented in real life. And if these ORM classes are used as your core "models", then good luck ever switching frameworks down the line. The ORM dependency is now tightly coupled to all your logic. You will effectively need to re-write your entire application. I have also seen the same mistake occur for gRPC generated data access classes. Best use a clean architecture and leave these dependencies out of your core business logic 😌&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;Am I an expert in DDD? Nope. But some of the insights above motivated me to apply it more, and to keep learning. If you feel the same, I've listing some additional resources below.&lt;br&gt;
&lt;a href="https://www.domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf" rel="noopener noreferrer"&gt;Domain-Driven Design Reference&lt;/a&gt; - Eric Evans&lt;br&gt;
&lt;a href="https://www.oreilly.com/library/view/domain-driven-design-tackling/0321125215/" rel="noopener noreferrer"&gt;Domain-Driven Design&lt;/a&gt; - Eric Evans&lt;br&gt;
&lt;a href="https://www.oreilly.com/library/view/implementing-domain-driven-design/9780133039900/" rel="noopener noreferrer"&gt;Implementing Domain-Driven Design&lt;/a&gt; - Vaughn Vernon&lt;br&gt;
&lt;a href="https://github.com/ddd-crew" rel="noopener noreferrer"&gt;Domain-Driven Design Crew GitHub repository&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.eventstorming.com/" rel="noopener noreferrer"&gt;EventStorming website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ddd</category>
      <category>cleancode</category>
      <category>programming</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>A Simple Guide for Choosing the Right Database</title>
      <dc:creator>Callum Lamont</dc:creator>
      <pubDate>Sun, 05 Jan 2025 21:47:20 +0000</pubDate>
      <link>https://dev.to/calamont/a-simple-guide-for-choosing-the-right-database-1920</link>
      <guid>https://dev.to/calamont/a-simple-guide-for-choosing-the-right-database-1920</guid>
      <description>&lt;p&gt;Why would you pick PostgreSQL to support your next application when you have dozens of alternatives to choose from?&lt;/p&gt;

&lt;p&gt;Databases are a tool to be leveraged by your application. But you should select the right tool for the job. You wouldn’t stubbornly use a hammer for every DIY project because you already know how a hammer works.&lt;/p&gt;

&lt;p&gt;So which database should you use? Of course, this is influenced by many factors. But above all, &lt;strong&gt;ask what type of data model aligns with your application&lt;/strong&gt;. Why? Because this is a strong indicator for how efficiently a database can organise and retrieve your data. Other technical and practical reasons can then be considered in your final technology choice.&lt;/p&gt;

&lt;p&gt;If you would like to learn more after reading this piece I would &lt;strong&gt;strongly&lt;/strong&gt; recommend Designing Data-Intensive Applications by Martin Kleppmann. I reference other useful resources at the end of the article for the eager.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a data model?
&lt;/h2&gt;

&lt;p&gt;A data model describes how data is structured and logically organised in a database. For example, SQL databases typically use a relational model, where relations are stored in separate tables. The data in these tables are then stored physically separate on disk. Clearly this impacts how reads and writes are performed, and may not be ideal for your application.  Other data models include document, wide-column and graph models. In certain use cases, it is also possible to effectively ignore the data model altogether!&lt;/p&gt;

&lt;p&gt;Data models are not interchangeable. Each makes assumptions about how the data is structured and will be accessed. A mismatch between the data model of your database and your application will impact performance. So, when building a new application, ask yourself these questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Will the written data adhere to a strict schema?&lt;/li&gt;
&lt;li&gt;Will the data have many-to-many relations?&lt;/li&gt;
&lt;li&gt;Is it useful to retrieve or update only part of the data? Or will you read and write it in full?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will see how the answers to these questions can help you identify the right data model and database.&lt;/p&gt;

&lt;h2&gt;
  
  
  No data model (i.e. key-value stores)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Example databases:&lt;/strong&gt; Riak, RocksDB&lt;/p&gt;

&lt;p&gt;Key-value stores offer the simplest approach to modelling data. Data exists, and is referenced by a unique key. That is pretty much it. Not all key-value stores are quite that ambivalent. Some offer slightly more query support for certain data types. But, as an oversimplification, there is no data model.&lt;/p&gt;

&lt;p&gt;RocksDB is an extreme example of this strategy. It is a key-value store where both the key and values are arbitrary byte arrays. These are stored together in an index. The database makes no assumptions what is being stored, how it is structured, and how it might be queried. Conceptually, stored records would look like this:&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%2Ftrrzur8caanrv0r8b7jt.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%2Ftrrzur8caanrv0r8b7jt.png" alt="Image description" width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;
RocksDB stores key-value pairs together in an LSM tree index. Other key-value stores may store data differently.



&lt;p&gt;This design benefits write throughput. There is only a primary index, which typically uses an LSM tree. To generalise, this append only data structure should write faster than the commonly used B-tree index. Further, the database doesn’t need to update secondary indexes on writes. It doesn’t even validate constraints or data formatting for the written value. Making the data in each row independent of other rows helps database partitioning. This can greatly improve scalability.&lt;/p&gt;

&lt;p&gt;The flexibility of the stored value could be a benefit for certain use cases. But such an agnostic approach has trade-offs that make it unsuitable for most applications. Clearly, there is no database support for joins between related data. The database is ignorant about what's stored for any key. It is not even possible to to perform simple updates without first reading the value into the application logic. For example, RocksDB cannot natively increment a counter or append to an array. The required Read-Modify-Write operations to update a value has obvious performance implications. It also introduces concurrency headaches if multiple threads update the same key.&lt;/p&gt;

&lt;p&gt;When is no data model suitable for your application?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you need &lt;strong&gt;very high&lt;/strong&gt; performance.&lt;/li&gt;
&lt;li&gt;You are dumping arbitrary data and the written records are independent from one another.&lt;/li&gt;
&lt;li&gt;The data will always be written and read in full during runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The document model
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Example databases:&lt;/strong&gt; MongoDB, CouchDB&lt;/p&gt;

&lt;p&gt;The document model model represents data as a semi-structured object, commonly JSON or XML. This model is a more structured extension of the key-value store. There is a primary key, and the value is the semi-structured object. However, because this value can be a rich nested document, and include secondary indexes, its value is typically stored separately on disk to the indexed keys.&lt;/p&gt;

&lt;p&gt;The document model is suitable if reading and writing self-contained data, which only has one-to-one or one-to-many relations. This allows the entire object to be stored together, in one location on disk. Consider this record for a holiday lodging website:&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%2F595vafkddbpwtw1sbk95.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%2F595vafkddbpwtw1sbk95.png" alt="Image description" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;
Most document databases store primary keys in a B-tree index, which points to the disk location where the document is written.



&lt;p&gt;Here the fields contain one-to-one and one-to-many relations. By storing the object as a single document, access to the full object is provided through a simple, efficient, atomic query. No joins necessary!&lt;/p&gt;

&lt;p&gt;Indeed, document databases typically have weak support for joins. It is discouraged to maintain any relation through foreign keys to other document collections. The preferred approach is to denormalise the data and embed it in all the relevant primary documents. By denormalising data, we remove the impediment of document joins and improve read performance. But there are downsides. Such an approach is detrimental if we expected the embedded data to change frequently. This is because the embedded data may be duplicated across many documents. So we need to update all of these if any of the embedded data is updated. On top of the obvious performance implications, this increases the chances of storing stale data, which could introduce bugs. So, a document model is ill-suited to modelling data with many-to-many relations.&lt;/p&gt;

&lt;p&gt;Document databases are usually schema-less, and the data can have arbitrary fields and levels of nesting. The lack of schema may be an attractive feature in some instances. However, for most applications there is an assumed schema. If it isn’t enforced by the database, it will be enforced in the application code. Martin Kleppmann refers to this as “schema on read”. As this schema evolves, the application code has to accommodate both old and new schemas of the stored data. The result can be messy and difficult to maintain code. Therefore, I don’t believe the “flexibility” of a schema-less database provides a real benefit. It’s simply a property of the database to be aware of.&lt;/p&gt;

&lt;p&gt;When is a document model suitable for your application?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your data is self-contained (only one-to-one or one-to-many relations) and is typically queried as the whole object.&lt;/li&gt;
&lt;li&gt;Your data truly doesn’t follow a fixed schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The relational model
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Example databases:&lt;/strong&gt; PostgreSQL, MySQL&lt;/p&gt;

&lt;p&gt;The relational model separates related data into separate tables, as opposed to storing the entire object in one location. There is less overhead if only part of the data needs to be updated or read. Less data needs to be read into the database’s memory or sent over the network. Splitting an object into multiple tables isn’t limited to many-to-many relations. It can be useful for one-to-many, and even one-to-one relations too. The best schema design depends on the use case and expected query patterns.&lt;/p&gt;

&lt;p&gt;Unsurprisingly, the relational model is useful for data that contains many-to-many relationships. Reading such data is more efficient as relational databases have good support for joins between tables. Also, separating objects into tables normalises the data. So our write efficiency is improved if we need to update a relation. Unlike unnormalised document databases, now we only need to update a single record, instead of every record that references that relation. Below we see the same property listing data as before, but split into a number of different tables. These reference each other through  foreign keys.&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%2Fia3vbp2ssaav4i9brx45.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%2Fia3vbp2ssaav4i9brx45.png" alt="Image description" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;
SQL splits relations into separate tables. Each of these have a separate index for its primary key, and probably secondary indexes to aid with joins.



&lt;p&gt;However, these relations impact write performance. Each table has a separate index, which needs to be updated every time the table is updated. There may be many secondary indexes to aid table joins. These, too, need to be updated on table writes. The relation model also limits performance if we frequently read or write the full object. Depending on the schema design, this may require the database to join multiple tables. While relational databases have good support for joins, this operation still carries a performance hit.&lt;/p&gt;

&lt;p&gt;As relational databases improve support for JSON/XML data types, they can leverage some of the benefits of document databases. Certain fields may be defined as a schema-less JSON object, which makes it easier to evolve these as the app matures. An example is the &lt;code&gt;details&lt;/code&gt; field in the above &lt;strong&gt;Listing&lt;/strong&gt; table. Through this feature, we can gain some of the advantages of a document-oriented database. For this reason, Andrew Pavlo goes so far as to suggest that SQL and a relational model is &lt;a href="https://www.youtube.com/watch?v=8Woy5I511L8&amp;amp;list=WL&amp;amp;index=3&amp;amp;t=35s" rel="noopener noreferrer"&gt;the only form of database you should consider for your app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When is a relational model suitable for your application?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You will frequently read only slices of the data, as opposed to requiring the full object.&lt;/li&gt;
&lt;li&gt;Your data contains many-to-many relations. Particularly, if you expected this related data to be updated frequently.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deciding on a database
&lt;/h2&gt;

&lt;p&gt;Again, ask yourself these questions when considering what data model, and therefore database, best suits your application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will the written data follow a regular schema?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If not, maybe a key-value store or document database could be useful. But be mindful how your application code will manage evolving data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will the data have many-to-many relations?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If the data has many-to-many relations, it is likely a relational database will serve you best. If it doesn't, think about the other types of relations that exist in the data. Is there a benefit extracting these into separate tables? Often there is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it useful to retrieve or update only part of the data? Or will you read and write it in full?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A relational structure is useful if only reading or updating part of the data. The database won’t need to read in the entire object when all that is required is a single field. The APIs of many document databases will allow you to retrieve and update a slice of data, but the database itself will need to read the full object into memory. For smaller applications, this overhead probably won’t be noticeable. But it is worth considering as your application grows. And for many key-value stores, updating a slice of data isn’t possible at all. The full object will need to be updated through the application code.&lt;/p&gt;

&lt;p&gt;I have summarised the most common data models for online transaction processing (OLTP). Only consider other options if it is obvious these are inadequate. For example, a graph database would clearly manage relations between users on a social media platform better. And ridiculous scaling requirements could force you to consider models that can be better distributed. In such situations, research, research some more, and use first principles to guide your decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;Here are some additional resources that describe the principles guiding different database designs. Some of these are quite academic, but will help you understand the technologies you work with every day as a developer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/" rel="noopener noreferrer"&gt;Designing Data-Intensive Applications&lt;/a&gt; - Martin Kleppman&lt;br&gt;
&lt;a href="https://db.cs.cmu.edu/papers/2024/whatgoesaround-sigmodrec2024.pdf" rel="noopener noreferrer"&gt;What Goes Around Comes Around... And Around...&lt;/a&gt; - Michael Stonebraker &amp;amp; Andrew Pavlo&lt;br&gt;
&lt;a href="https://www.seas.upenn.edu/~zives/03f/cis550/codd.pdf" rel="noopener noreferrer"&gt;A Relational Model of Data for Large Shared Data Banks&lt;/a&gt; - Edgar F. Codd&lt;br&gt;
&lt;a href="https://www.cs.umb.edu/~poneil/lsmtree.pdf" rel="noopener noreferrer"&gt;The Log-Structured Merge Tree - (LSM-Tree)&lt;/a&gt; - Patrick O’Neil et al.&lt;br&gt;
&lt;a href="https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf" rel="noopener noreferrer"&gt;Dynamo: Amazon’s Highly Available Key-value Store&lt;/a&gt; - Giuseppe DeCandia et al.&lt;br&gt;
&lt;a href="https://static.googleusercontent.com/media/research.google.com/en//archive/bigtable-osdi06.pdf" rel="noopener noreferrer"&gt;Bigtable: A Distributed Storage System for Structured Data&lt;/a&gt; - Fay Chang et al.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>mongodb</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>How to Setup Vim for Kotlin Development</title>
      <dc:creator>Callum Lamont</dc:creator>
      <pubDate>Fri, 27 Dec 2024 21:05:03 +0000</pubDate>
      <link>https://dev.to/calamont/how-to-setup-vim-for-kotlin-development-4eoc</link>
      <guid>https://dev.to/calamont/how-to-setup-vim-for-kotlin-development-4eoc</guid>
      <description>&lt;p&gt;The sad truth is that JVM languages are awkward to use with Vim. This is compounded for Kotlin. The user base is still smaller than Java's, so there are far fewer how-to guides to help set up your editor. Below should serve as a good crash course for developing Kotlin in Vim. Who needs to use IntelliJ 💁&lt;/p&gt;

&lt;p&gt;So, to enable a reasonable Kotlin developer experience, we need to install:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Kotlin grammar file to enable syntax highlighting.&lt;/li&gt;
&lt;li&gt;The Kotlin language server (for IntelliSense and code navigation).&lt;/li&gt;
&lt;li&gt;A code completion engine (optional, for Neovim) to auto-import dependencies.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These instructions have been written primarily for Neovim, but includes alternatives if you’d prefer to use the OG Vim.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;A Vim plugin manager.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You &lt;em&gt;can&lt;/em&gt; manage plugins with Git submodules, but I've found this to be a painful experience.&lt;/li&gt;
&lt;li&gt;Some examples are &lt;a href="https://github.com/junegunn/vim-plug" rel="noopener noreferrer"&gt;vim-plug&lt;/a&gt;, &lt;a href="https://github.com/VundleVim/Vundle.vim" rel="noopener noreferrer"&gt;vundle&lt;/a&gt;, or, &lt;a href="http://www.lazyvim.org/" rel="noopener noreferrer"&gt;lazy.nvim&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An LSP client.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Neovim comes with a client. For Vim you will need to install one, such as &lt;a href="https://github.com/neoclide/coc.nvim" rel="noopener noreferrer"&gt;CoC&lt;/a&gt;, &lt;a href="https://github.com/autozimu/LanguageClient-neovim" rel="noopener noreferrer"&gt;LanguageClient-neovim&lt;/a&gt;, or &lt;a href="https://github.com/prabirshrestha/vim-lsp" rel="noopener noreferrer"&gt;vim-lsp&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Gradle.&lt;/li&gt;
&lt;li&gt;Java 11+ and Kotlin. Currently there is an &lt;a href="https://github.com/fwcd/kotlin-language-server/issues/604" rel="noopener noreferrer"&gt;open issue&lt;/a&gt; for Kotlin versions greater than 2.0.0. Downgrade if you have any issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Syntax Highlighting
&lt;/h2&gt;

&lt;p&gt;Because Kotlin isn’t as mainstream as Python or Java, many editors don’t have a default grammar to enable syntax highlighting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Neovim
&lt;/h3&gt;

&lt;p&gt;Neovim could use the default regex based grammar that Vim uses for syntax highlighting. But I recommended installing &lt;a href="https://github.com/nvim-treesitter/nvim-treesitter" rel="noopener noreferrer"&gt;treesitter&lt;/a&gt;. This uses a concrete syntax tree to provide more semantic meaning to tokens, allowing enriched (and faster) highlighting.&lt;/p&gt;

&lt;p&gt;Once treesitter is installed (using the instructions in the repository), install the Kotlin grammar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="p"&gt;:&lt;/span&gt;TSInstall kotlin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And update your &lt;code&gt;init.lua&lt;/code&gt; file to turn on treesitter synax highlighting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;treesitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'nvim-treesitter.configs'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;treesitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;highlight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;additional_vim_regex_highlighting&lt;/span&gt; &lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will have beautiful syntax highlighting when you next open Neovim 🌈&lt;/p&gt;

&lt;h3&gt;
  
  
  Vim
&lt;/h3&gt;

&lt;p&gt;kotlin-vim is a reasonably popular plugin that provides syntax highlighting, among other functionalities. This enables highlighting by providing Vim a &lt;a href="https://github.com/udalov/kotlin-vim/blob/master/syntax/kotlin.vim" rel="noopener noreferrer"&gt;file of regex based Kotlin grammar&lt;/a&gt;. Once installed, it should enable syntax highlighting without further configuration.&lt;/p&gt;

&lt;p&gt;The usefulness of the syntax highlighting also depends on your chosen colourscheme. Have a look at &lt;a href="https://vimcolorschemes.com/" rel="noopener noreferrer"&gt;this collection&lt;/a&gt; to find new schemes to try out! &lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Language Server
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installing the Language Server
&lt;/h3&gt;

&lt;p&gt;The Kotlin language server is packaged in few places, but I recommend to build it from the source code. This helps you understand how the server and client are orchestrated. It will also aid debugging if anything goes wrong.&lt;/p&gt;

&lt;p&gt;First, clone the repository. I suggest keeping all language servers in one directory &lt;code&gt;$HOME/language_servers&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/language_servers
&lt;span class="nv"&gt;$ &lt;/span&gt;git clone git@github.com:fwcd/kotlin-language-server.git
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;kotlin-language-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the build and tests work as expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./gradlew clean build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build the project and binaries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./gradlew installDist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There should now be an executable &lt;code&gt;kotlin-language-server&lt;/code&gt; binary in &lt;code&gt;$HOME/language_servers/build/install/bin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For a large project, the Kotlin language server has a slow start up time (a few minutes) while it tokenises your entire project and stores these symbols in a database.&lt;/p&gt;

&lt;p&gt;The server is also rather flakey. At some point while using it, it is likely to throw an error and stop working. Just close Vim and reopen your buffers again 💁&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring the LSP Client
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Neovim&lt;/strong&gt;&lt;br&gt;
Install the &lt;a href="https://github.com/neovim/nvim-lspconfig" rel="noopener noreferrer"&gt;nvim-lspconfig&lt;/a&gt; plugin to make configuration of the LSP client simpler. Then update your &lt;code&gt;init.lua&lt;/code&gt; to tell Neovim which filetypes to use the language server for (i.e. &lt;code&gt;.kt&lt;/code&gt; and &lt;code&gt;.kts&lt;/code&gt; extensions). The LSP client will try to the start the sever by running the &lt;code&gt;kotlin_language_server&lt;/code&gt; binary. You can directly point Neovim to the location of the binary, or include this directory in your &lt;code&gt;$PATH&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;lsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'lspconfig'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;lsp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kotlin_language_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;filetypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"kotlin"&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"kt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"kts"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;-- If you don't update you $PATH&lt;/span&gt;
  &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;os.getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"HOME"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;"/language_servers/build/install/bin/kotlin_language_server"&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;&lt;strong&gt;Vim&lt;/strong&gt;&lt;br&gt;
Follow the configuration instructions on the GitHub repo of your chosen LSP client (each one will be different). The Kotlin language server repository also provides some &lt;a href="https://github.com/fwcd/kotlin-language-server/blob/main/EDITORS.md#vim" rel="noopener noreferrer"&gt;sample configuration&lt;/a&gt; for CoC and LanguageClient-neovim.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Completion Engine (Neovim only)
&lt;/h2&gt;

&lt;p&gt;The builtin Vim omni completion engine is simple, but surprisingly useful. It will allow you to autocomplete any existing word in your open buffers (which is sufficient for a lot of use cases). But the completion menu won’t include options from your project dependencies.&lt;/p&gt;

&lt;p&gt;With a language server set up, we have broader, project-wide context at our disposal. If we can plug this intelligence into our completion engine, it would then be possible to autocomplete and auto-import any method or class that exists in our project or its dependencies.&lt;/p&gt;

&lt;p&gt;For this, we need to install a new completion engine: &lt;a href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer"&gt;nvim-cmp&lt;/a&gt;. This engine uses completion “sources” (installed as separate dependencies) to populate the completion menu. So we also need to install &lt;a href="https://github.com/hrsh7th/cmp-buffer" rel="noopener noreferrer"&gt;cmp-buffer&lt;/a&gt; and &lt;a href="https://github.com/hrsh7th/cmp-nvim-lsp" rel="noopener noreferrer"&gt;cmp-nvim-lsp&lt;/a&gt;. Check out &lt;a href="https://www.youtube.com/watch?v=_DnmphIwnjo" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt; by TJ DeVries for more info about how this plugin works.&lt;/p&gt;

&lt;p&gt;Once those three plugins are installed, add this configuration to your &lt;code&gt;init.lua&lt;/code&gt;. See the repository for more configuration options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;mapping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;C-n&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select_next_item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;behavior&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectBehavior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Insert&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;C-p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select_prev_item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;behavior&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectBehavior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Insert&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;C-u&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scroll_docs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;C-d&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scroll_docs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;C-o&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;complete_common_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;C-e&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;C-y&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'buffer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;-- only start autocompleting after a few chars typed&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nvim_lsp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_item_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;-- don't overpopulate list with symbols from LSP&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;-- Just for aesthetics&lt;/span&gt;
  &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bordered&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;documentation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bordered&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;Now, when you hit CTRL-n will open a completion menu. Pressing CTRL-y on an option will autocomplete with that symbol and (if required) insert the needed import statement at the top of your file 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;Following the above configuration should provide you syntax highlighting, LSP functionality (diagnostics, go to definition, etc.), and auto import.&lt;br&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%2Fr43uz3ue6581e2hfnphj.gif" 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%2Fr43uz3ue6581e2hfnphj.gif" alt="Kotlin development, in action" width="760" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even with the above configuration, developing Kotlin in Vim isn't the smoothest experience. This is mostly due to limitations with the Kotlin LSP. I hope community support will improve this over time 🙂&lt;/p&gt;

&lt;p&gt;Here is a fully functioning &lt;a href="https://gist.github.com/calamont/d5572c8d6cdb0ce2ce4d6f71873758b3" rel="noopener noreferrer"&gt;init.lua file&lt;/a&gt; for Neovim that includes all the configuration for the the above plugins.&lt;/p&gt;

</description>
      <category>neovim</category>
      <category>kotlin</category>
      <category>lsp</category>
      <category>vim</category>
    </item>
  </channel>
</rss>
