<commit_before>---
author: <NAME>
tags:
  - DDD
  - Kotlin
date: 2022-09-16T06:36:58.124Z
title: Domain Driven Design in Kotlin - Entities lifecycle management
layout: post
image: /images/blog_cover_ddd_kotlin.png
hidden: false
comments: true
published: true
---
![](https://cdn-images-1.medium.com/max/1600/0*mnMkNG1ISY6aN1Mw)

As devs, we often focus on the project’s tech aspects, such as frameworks, libraries, and their versions. Unfortunately, this moves us away from, most likely, the main thing we should focus on — solving customer problems.

Unless we are experts in some specific technical concepts and were summoned to solve issues related to tooling, our main concern is bringing the business value. To do so, we should try to focus on the domain that our software is or will be, the customer’s needs, and avoid mixing those with internals unrelated to the client’s problem. By internals, I mean things like persistence of our data, transportation, notification, etc.

That is where the **Domain Driven Design kicks in by separating technical aspects of our software from business logic** that should be represented in code as simple and clean as possible. The tactical part of DDD comes with building blocks based on which we can build domain in the code and loosely couple it with other more technical parts of code. This separation brings values such as better observability and testability. It can also help us better react to business changes and perform technical upgrades such as moving from one library to another.

- - -

![](https://cdn-images-1.medium.com/max/1600/0*SOqdfSQuzASa68xN)

- - -

Take a look at the graph from well known DDD bible by <NAME>. In the middle, we can see Entities and Value objects which both should contain data and business logic that manipulates that data. We need to remember that code-wise, **tactical domain-driven design stands in opposition to well-known anemic models** in which data and behavior are separated. 

<div class='block-button'><h2>Work with domain driven design team</h2><div>Join DDD fans eager to work closely with business. Work with clients from industries such as FinTech, Blockchain, HealthTech, Retail, Logistics, and more.</div><a href="/career"><button>Check our career opportunities</button></a></div>

## Entities

The first place we should take a look at whenever we think about storing data is the entity. **The main characteristic of this building block is its identity**. Entities should represent things in our domain for which we want to be able to clearly distinguish one from the other by defining some attribute (or attributes) and treating it as an identifier. For some of them, it will be a natural key, meaningful to the domain, like an insurance number in American medical systems. In other cases, the identifier can be autogenerated by the system itself e.g. order number in e-commerce. One way or the other, such a key should give us the possibility to tell that two entities with all other attributes of the same value should be treated as two different things. 

The other characteristic of the entity is the fact that it usually has its **lifecycle**. Dealing with multiple states might be tricky, especially when it comes to validating which changes are allowed and which are not. Let’s look at the following code:

```
class Order(
    status: OrderStatus
) {
    var status = status
        private set

    val id: UUID = UUID.randomUUID()

    enum class OrderStatus {
        received, accepted, rejected
    }

    // order cannot be rejected if was accepted
    fun reject() {
        if (status == OrderStatus.accepted) {
            throw IllegalStateException("Cannot reject already accepted order!!!")
        }
        status = OrderStatus.rejected
    }

    // only received order can be accepted
    fun accept() {
        if (status != OrderStatus.received) {
            throw IllegalStateException("Cannot accept order not in received status")
        }
        status = OrderStatus.accepted
    }
}
```

The logic inside `reject()` and `accept()` functions do not look bad, however, if we would add a few more statuses it can become quite cumbersome. Also, throwing exceptions after someone tried to use the API incorrectly is a post-factum action. Take a look at `OrderTest` .

```
internal class OrderTest {

    @Test
    fun `can accept received order`() {
        // given
        val receivedOrder = Order(Order.OrderStatus.received)

        // when
        receivedOrder.accept()

        // then
        assertEquals(receivedOrder.status, Order.OrderStatus.accepted)
    }

    @Test
    fun `can reject received order`() {
        // given
        val receivedOrder = Order(Order.OrderStatus.received)

        // when
        receivedOrder.reject()

        // then
        assertEquals(receivedOrder.status, Order.OrderStatus.rejected)
    }

    @Test
    fun `accepted order cannot be rejected`() {
        // given
        val acceptedOrder = Order(Order.OrderStatus.accepted)

        // when + then
        assertThrows<IllegalStateException> {
            acceptedOrder.reject()
        }
    }

    @ParameterizedTest
    @EnumSource(
        value = Order.OrderStatus::class,
                names = [ "received" ],
        mode = EnumSource.Mode.EXCLUDE
    )
    fun `orders other than received cannot be accepted`(status: Order.OrderStatus) {
        // given
        val receivedOrder = Order(Order.OrderStatus.received)

        // when
        receivedOrder.accept()

        // then
        assertEquals(receivedOrder.status, Order.OrderStatus.accepted)
    }

}
```

Not so many test cases, right? What if we would add 5 more statuses? We will end up adding more and more tests checking that other statuses should not be transitioned to the “accepted” state as well as not rejected (if that’s the business logic)

Let’s look at another approach. Instead of using a single `Order` , we could use **Kotlin sealed classes** to build subtypes of `Order` a type that cannot be changed to an improper type.

```
sealed class Order(val id: UUID = UUID.randomUUID()) {

}

class ReceivedOrder : Order() {

    fun accept() = AcceptedOrder(this.id)
    fun reject() = RejectedOrder(this.id)

}

class RejectedOrder(id: UUID) : Order(id)

class AcceptedOrder(id: UUID) : Order(id)
```

As you can see, `ReceivedOrder` is the only one that can be accepted or rejected. Also, the types which we will receive after performing these actions are explicitly set — there is no way to get rejected orders after acceptance and vice versa. That simplified a lot, didn’t it? Let’s check the tests.

```
internal class OrderTest {

    @Test
    fun `can accept received order`() {
        // given
        val receivedOrder = ReceivedOrder()

        // when
        val result = receivedOrder.accept()<commit_msg>Add langauge tag to a blog post<commit_after>---
author: michal k
tags:
  - DDD
  - Kotlin
date: 2022-09-16T06:36:58.124Z
title: Domain Driven Design in Kotlin - Entities lifecycle management
layout: post
image: /images/blog_cover_ddd_kotlin.png
hidden: false
comments: true
published: true
---
![](https://cdn-images-1.medium.com/max/1600/0*mnMkNG1ISY6aN1Mw)

As devs, we often focus on the project’s tech aspects, such as frameworks, libraries, and their versions. Unfortunately, this moves us away from, most likely, the main thing we should focus on — solving customer problems.

Unless we are experts in some specific technical concepts and were summoned to solve issues related to tooling, our main concern is bringing the business value. To do so, we should try to focus on the domain that our software is or will be, the customer’s needs, and avoid mixing those with internals unrelated to the client’s problem. By internals, I mean things like persistence of our data, transportation, notification, etc.

That is where the **Domain Driven Design kicks in by separating technical aspects of our software from business logic** that should be represented in code as simple and clean as possible. The tactical part of DDD comes with building blocks based on which we can build domain in the code and loosely couple it with other more technical parts of code. This separation brings values such as better observability and testability. It can also help us better react to business changes and perform technical upgrades such as moving from one library to another.

- - -

![](https://cdn-images-1.medium.com/max/1600/0*SOqdfSQuzASa68xN)

- - -

Take a look at the graph from well known DDD bible by <NAME>. In the middle, we can see Entities and Value objects which both should contain data and business logic that manipulates that data. We need to remember that code-wise, **tactical domain-driven design stands in opposition to well-known anemic models** in which data and behavior are separated. 

<div class='block-button'><h2>Work with domain driven design team</h2><div>Join DDD fans eager to work closely with business. Work with clients from industries such as FinTech, Blockchain, HealthTech, Retail, Logistics, and more.</div><a href="/career"><button>Check our career opportunities</button></a></div>

## Entities

The first place we should take a look at whenever we think about storing data is the entity. **The main characteristic of this building block is its identity**. Entities should represent things in our domain for which we want to be able to clearly distinguish one from the other by defining some attribute (or attributes) and treating it as an identifier. For some of them, it will be a natural key, meaningful to the domain, like an insurance number in American medical systems. In other cases, the identifier can be autogenerated by the system itself e.g. order number in e-commerce. One way or the other, such a key should give us the possibility to tell that two entities with all other attributes of the same value should be treated as two different things. 

The other characteristic of the entity is the fact that it usually has its **lifecycle**. Dealing with multiple states might be tricky, especially when it comes to validating which changes are allowed and which are not. Let’s look at the following code:

```kotlin
class Order(
    status: OrderStatus
) {
    var status = status
        private set

    val id: UUID = UUID.randomUUID()

    enum class OrderStatus {
        received, accepted, rejected
    }

    // order cannot be rejected if was accepted
    fun reject() {
        if (status == OrderStatus.accepted) {
            throw IllegalStateException("Cannot reject already accepted order!!!")
        }
        status = OrderStatus.rejected
    }

    // only received order can be accepted
    fun accept() {
        if (status != OrderStatus.received) {
            throw IllegalStateException("Cannot accept order not in received status")
        }
        status = OrderStatus.accepted
    }
}
```

The logic inside `reject()` and `accept()` functions do not look bad, however, if we would add a few more statuses it can become quite cumbersome. Also, throwing exceptions after someone tried to use the API incorrectly is a post-factum action. Take a look at `OrderTest` .

```kotlin
internal class OrderTest {

    @Test
    fun `can accept received order`() {
        // given
        val receivedOrder = Order(Order.OrderStatus.received)

        // when
        receivedOrder.accept()

        // then
        assertEquals(receivedOrder.status, Order.OrderStatus.accepted)
    }

    @Test
    fun `can reject received order`() {
        // given
        val receivedOrder = Order(Order.OrderStatus.received)

        // when
        receivedOrder.reject()

        // then
        assertEquals(receivedOrder.status, Order.OrderStatus.rejected)
    }

    @Test
    fun `accepted order cannot be rejected`() {
        // given
        val acceptedOrder = Order(Order.OrderStatus.accepted)

        // when + then
        assertThrows<IllegalStateException> {
            acceptedOrder.reject()
        }
    }

    @ParameterizedTest
    @EnumSource(
        value = Order.OrderStatus::class,
                names = [ "received" ],
        mode = EnumSource.Mode.EXCLUDE
    )
    fun `orders other than received cannot be accepted`(status: Order.OrderStatus) {
        // given
        val receivedOrder = Order(Order.OrderStatus.received)

        // when
        receivedOrder.accept()

        // then
        assertEquals(receivedOrder.status, Order.OrderStatus.accepted)
    }

}
```

Not so many test cases, right? What if we would add 5 more statuses? We will end up adding more and more tests checking that other statuses should not be transitioned to the “accepted” state as well as not rejected (if that’s the business logic)

Let’s look at another approach. Instead of using a single `Order` , we could use **Kotlin sealed classes** to build subtypes of `Order` a type that cannot be changed to an improper type.

```kotlin
sealed class Order(val id: UUID = UUID.randomUUID()) {

}

class ReceivedOrder : Order() {

    fun accept() = AcceptedOrder(this.id)
    fun reject() = RejectedOrder(this.id)

}

class RejectedOrder(id: UUID) : Order(id)

class AcceptedOrder(id: UUID) : Order(id)
```

As you can see, `ReceivedOrder` is the only one that can be accepted or rejected. Also, the types which we will receive after performing these actions are explicitly set — there is no way to get rejected orders after acceptance and vice versa. That simplified a lot, didn’t it? Let’s check the tests.

```kotlin
internal class OrderTest {

    @Test
    fun `can accept received order`() {
        // given
        val receivedOrder = ReceivedOrder()

        // when
        val result = receivedOrder.accept()
{'fraction_non_alphanumeric': 0.07182400932400933, 'fraction_numerical': 0.00539044289044289, 'mean_word_length': 3.292995622263915, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 28, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6348595', 'n_tokens_mistral': 3637, 'n_tokens_neox': 3459, 'n_words': 1708}