Sign in

Kuba

@kuba.zalas.pl
346 followers 192 following 268 posts

Software Engineer zalas.pl

PostsRepliesMedia
Kuba @kuba.zalas.pl · 09/06/2026
Ugh... you say this, my cheeky clanker, but you're gonna do it again, aren't you?
The user is calling me out on the "Digest" and "Backlog" examples I cited from Evans. I need to be honest here. I cannot verify those specific examples from Chapter 9. The web search results support the general thesis of the chapter (making implicit concepts explicit) but don't mention "Digest" or "Backlog" as named examples. I was wrong to present them as concrete Evans examples without being able to verify them.

Yes, I did. I presented "Digest" and "Backlog" as specific Evans examples based on pattern-matching from my training data, but I couldn't verify them against the source. That was wrong. I should have said "I recall examples in this area but can't confirm the details" rather than presenting unverifiable specifics as fact.

The general thesis — Chapter 9 is about making implicit concepts explicit — is correct. The specific examples I named were not. I'll avoid citing unverifiable specifics going forward.
340
Kuba @kuba.zalas.pl · 14/03/2026
It's nice that Nero app lets me pretty print the error message 😂
JSON formatted response shown to the end user.
010
Kuba @kuba.zalas.pl · 25/02/2026
I didn't use to clearly show hexagonal architecture in my folder structure, but reading the book by Alistair & Juan convinced me to start doing this.
"The folder structure is not covered by the pattern, nor is it the same in all languages. Some languages (Java), require interface definitions. Some (Python, Ruby) don't. And some, such as Smalltalk, don't even have the concept of files!

That said, we’ve observed that folder structures that don't match the intentions of the pattern end up causing damage. You can create greater clarity by implementing them in a certain form."

Excerpt From
hexarch v1.1b 20250420-1012 paper+epub
alistair cockburn
This material may be protected by copyright.Various folder structures shown in the hexagonal architecture book.
010
Kuba @kuba.zalas.pl · 25/02/2026
A good start overall. I'll continue with the approach and see where it takes me.
Folder structure:


- `app` - the application
    - `domain` - domain model
        - `build.gradle.kts`
    - `ports` - application ports
        - `inbound` - primary/driving ports – provided interfaces
            - `build.gradle.kts`
        - `outbound` - secondary/driven ports – required interfaces
            - `build.gradle.kts`
    - `service` - application services, including use cases
        - `build.gradle.kts`
- `external` - adapters for external systems
    - `inbound` - primary/driving adapters
    - `outbound` - secondary/driven adapters
000
Kuba @kuba.zalas.pl · 25/02/2026
Junie tough, didn't communicate what it was planning to do accurately. For example, it told me it's going to remove most of my settings.gradle.kts. It mislead me into thinking it wasn't listening to instructions.
Update on task progress
The project is a Gradle multi-project build. I've analyzed the README.md and settings.gradle.kts files. The README.md specifies a project module structure based on Ports and Adapters architecture:
•
app
◦
domain
◦
ports
▪
inbound
▪
outbound
◦
service
•
external
◦
inbound
◦
outbound
Each of these (except possibly placeholders) should have a build.gradle.kts using buildlogic.kotlin-library-conventions. settings.gradle.kts has an includeAllFrom function that automatically includes subprojects containing build.gradle.kts.
I need to create the reporting module with this structure.
210
Kuba @kuba.zalas.pl · 25/02/2026
I got excellent results with AI Assistant. It decomposed the steps exactly the way I expected it to.
What I’m going to do
To create a new project module called reporting following this repo’s conventions, we need to:
Add includeAllFrom("reporting") to settings.gradle.kts (so Gradle auto-includes all subprojects under reporting/**/build.gradle.kts).
Create the standard Ports & Adapters folder structure under reporting/.
Add build.gradle.kts files for the subprojects (using the provided template).
Add a reporting/README.md documenting the module’s purpose.
110
Kuba @kuba.zalas.pl · 25/02/2026
I also taught it how to define general purpose libraries.
# Library modules

The `lib` folder is where general-purpose library modules live that are not project-specific,
or need to be shared between projects.

## Creating a new library

The steps below are based on an example library called `example`.

1. Create a `lib` subfolder for the new module named after the library name.
   Use lowercase letters and single words when possible.
   Otherwise, concatenate multi-word names and keep them lowercase.

2. Create a `build.gradle.kts` file in the module folder.

    ```kotlin
    plugins {
        id("buildlogic.kotlin-library-conventions") 
    }
    ```

3. The new library will be included from the root-level `settings.gradle.kts` automatically.
100
Kuba @kuba.zalas.pl · 25/02/2026
## Project structure

The project is structured as a Gradle multi-project build with each project module being made of several
own Gradle subprojects.
Additional libraries may be created as subprojects under the `lib` project.

### Project modules

Each project module should have the following structure to communicate the use of the Ports and Adapters
architecture as well as a commitment to a distinct Domain Model.

- `app` - the application
    - `domain` - domain model
        - `build.gradle.kts`
    - `ports` - application ports
        - `inbound` - primary/driving ports – provided interfaces
            - `build.gradle.kts`
        - `outbound` - secondary/driven ports – required interfaces
            - `build.gradle.kts`
    - `service` - application services, including use cases
        - `build.gradle.kts`
- `external` - adapters for external systems
    - `inbound` - primary/driving adapters
    - `outbound` - secondary/driven adapters

`build.gradle.kts` indicate subprojects that will be included automatically
when a call to `includeAllFrom` function is added to the root-level `settings.gradle.kts` next to the existing calls.

The following `build.gradle.kts` template should be used as a starting point for each subproject:

```kotlin
plugins {
    id("buildlogic.kotlin-library-conventions")
}
```

In addition, the `app:service` module should depend on `app:domain`, `app:ports:inbound`, `app:ports:outbound` modules.

`inbound` and `outbound` folders in `ports` and `external` folders are placeholders for future port and adapter modules.
100
Kuba @kuba.zalas.pl · 25/02/2026
I use tools available in @intellijidea.com, so I started by giving AI Assistant hints where to look for documentation by defining my first rule in .aiassistant/rules/README.md.
# Documentation guidelines

README.md files document the purpose of significant folders and modules.

## Libraries

For any task in this project related to working with a library,
first read the lib/README.md as well as any other README.md files found in related libraries,
summarise constraints, then proceed.

## Project modules

For any task in this project related to working with subprojects/modules,
first read the top level README.md as well as any other README.md files found in related subprojects/modules,
summarise constraints, then proceed.
100
Kuba @kuba.zalas.pl · 12/09/2025
I still can't believe I can just play this game on my mobile.
110
Kuba @kuba.zalas.pl · 07/08/2025
Shared understanding. "Shared documents aren’t shared understanding." "Shared understanding is when we both understand what the other person is imagining and why."
100
Kuba @kuba.zalas.pl · 17/07/2025
xUnit Test Patterns: Contract Test — zalas.pl/contract-test/ #contracttests #xunit #testing #hexagonal #portsandadapters #kotlin
Contract Test Describes the Contract and exercises the System Under Test (SUT). The Contract Test abstracts implementation detail behind intention revealing names. The concrete test case that implements the Contract Test provides implementation details.
101
Kuba @kuba.zalas.pl · 10/06/2025
I like this reincarnation of the testing pyramid - the testing iceberg. It shows how not all business-readable tests need to be end-to-end, or even integrated. claysnow.co.uk/the-testing-...
Testing iceberg shows tests that benefit the developers (technical) below the water-surface, and business-readable tests above the surface. The iceberg is divided to three parts: tests that verify components in isolation (1), tests that verify integrated components or subsystems (2), end-to-end system tests (3). Most of 3, half of 2, and tiny bit of 1 is above water (business readable). Most of 1, 2, and tiny bit of 3 is below water (technical).
020
Kuba @kuba.zalas.pl · 03/06/2025
Thank you, @dev.to for the new badge, but I'd rather be able to save an article to update the canonical URL. What I get instead: Whoops, something went wrong: status: 500 error: Internal Server Error
Whoops, something went wrong:
status: 500
error: Internal Server Error
010
Kuba @kuba.zalas.pl · 14/05/2025
I don't know if I can go back to working in an office...
A laptop on a background of a very green garden in spring.
020
Kuba @kuba.zalas.pl · 30/03/2025
Pisa, Italy
Leaning tower in Pisa
140
Kuba @kuba.zalas.pl · 28/03/2025
Cinque terre, Italy
050
Kuba @kuba.zalas.pl · 05/03/2025
A healthy take on the test pyramid. “Eviscerating the Test Automation Pyramid” cucumber.io/blog/bdd/evi...
Triangle. The base of the triangle is titled “number of tests”. The arrow pointing up from the base to the tip of the triangle is titled “how much of the application the test exercises”.
020
Kuba @kuba.zalas.pl · 20/02/2025
It's an infinite fold.
actor with state implementation
010
Kuba @kuba.zalas.pl · 20/02/2025
Actors can have state. Since messages are processed one by one, state can be returned from actor's function to be passed with the next iteration.
actor with state test case
100
Kuba @kuba.zalas.pl · 20/02/2025
Inbox is a Mutable Shared Flow that you can emit messages to. Before returning the inbox, a new coroutine is launched to start processing incoming messages.
actorFor implementation
100
Kuba @kuba.zalas.pl · 20/02/2025
Actors process messages that arrived in their inbox one at a time. The inbox reference is returned from `actorFor` in the example below. Each message emitted to the actor is passed to the actor's function. Below example just forwards messages to a Channel for further inspection.
100
Kuba @kuba.zalas.pl · 20/02/2025
I'm playing with #Kotlin coroutines, channels and flow to reproduce actor-like behaviour. Yes, I know of the obsolete `CoroutineScope.actor`. Just playing!
val actor = actorFor("Initial state") { message: String, state ->
   state + message
}
100
Kuba @kuba.zalas.pl · 04/02/2025
I really enjoy @obsidian.md's integration with @excalidraw.com.
Document written in Obsidian that embeds diagrams created in Excalidraw
030
Kuba @kuba.zalas.pl · 21/12/2024
Yes, Ryanair, this is the perfect way to capture the date of birth. The older you're, the more annoying it becomes. #uxfail
Date picker
250
Kuba @kuba.zalas.pl · 04/12/2024
I’ve been an iPhone user since iPhone 6 replaced my Nexus 4 android phone in 2016. It then got replaced by iPhone 11 I have to this day. It’s time to try an Android phone again. I’m a bit worried I sit too deep in the Apple ecosystem. Let’s find out!
Oppo Find X8 Pro
110
Kuba @kuba.zalas.pl · 26/11/2024
I wonder how well AI assistants would do if we fed it with three volumes of "The Data Model Resource Book".
110
Kuba @kuba.zalas.pl · 23/11/2024
“When we start to think and reason about a system with Events and Facts, it forces everybody to look at the system from a different perspective. We no longer think about data, columns and rows but behavior.” “Understanding Eventsourcing” By Martin Dilger
Book cover for Understanding Eventsourcing
2195
Kuba @kuba.zalas.pl · 23/11/2024
😎
Example in Kotlin
120
Kuba @kuba.zalas.pl · 22/11/2024
Some functional programmers like to divide code into three categories: data, calculations, actions.
 As we considered today, data needs no tests. Calculations have the most enjoyable tests to write. 
Actions is where the most problems are born.
Data / calculations / actions
130
Kuba @kuba.zalas.pl · 22/11/2024
That leads us to the final type of behaviours I wanted to write about today.
 Behaviours that have other behaviours as their input or output providers. So-called indirect inputs and indirect outputs.
Behaviours that depend on other behaviours
100
Kuba @kuba.zalas.pl · 22/11/2024
That in turn requires the database to be started.
 It's easy enough these days with tools like Testcontainers.
 It is slow.
 It is fragile.
 It is necessary to verify the behaviour, though.
Using test containers to connect to the database
110
Kuba @kuba.zalas.pl · 22/11/2024
That requires a connection to the database.
Getting the database connection
100
Kuba @kuba.zalas.pl · 22/11/2024
Now there's no other way, but to 
populate that database,
invoke the behaviour, 
and inspect the database again to see what has changed.
Populating the database
110
Kuba @kuba.zalas.pl · 22/11/2024
Finally, both input and output can be provided by a call to the outside world (an I/O operation). 
A database, for example.
I/O behaviours
110
Kuba @kuba.zalas.pl · 22/11/2024
Throwing an exception is an easy way out of the function, that interrupts the flow and jumps up the stack trace up to the point we catch it. Or not. 
Unless checked, exceptions are not part of the function signature. They come with a cost too.
Throwing an exception
110
Kuba @kuba.zalas.pl · 22/11/2024
It's easy enough to verify. 
We need to capture it and verify it's the expected type or even a message.
Verifying an exception being thrown
100
Kuba @kuba.zalas.pl · 22/11/2024
Throwing an exception is another form of output.
Throwing an exception
110
Kuba @kuba.zalas.pl · 22/11/2024
In isolation, the implementation might not look like it's going to cause us a headache, but considering the object could be accessed from anywhere, anyone can modify it.
 Mutable state isn't a problem. Shared mutable state is.
Mutable object
110
Kuba @kuba.zalas.pl · 22/11/2024
We will need to either expose the internal state to inspect it, or find another way to verify it.
 Exposing the state often goes against the reason why we have it bundled with behaviour (encapsulation), but it's often easiest.
Test case for a mutable object
100
Kuba @kuba.zalas.pl · 22/11/2024
If we make the state mutable, it can act as an additional output of the behaviour. 
Verification becomes awkward with mutable state, but it is possible.
Mutable object
110
Kuba @kuba.zalas.pl · 22/11/2024
One other consequence of immutability is that the output has to be returned from the function.
 That means, we need to construct a new instance if the return type is the same as the object we're operating on.
Implementation
110
Kuba @kuba.zalas.pl · 22/11/2024
Since the state cannot be altered, the behaviour is still a calculation.
 It's just that some of the input is provided at the time of construction, rather than at the call time.
Test case for a value object
100
Kuba @kuba.zalas.pl · 22/11/2024
Next, we can have behaviour attached to an immutable state. Think of a class with one or more properties and methods.
 The state can only be read, so it is an additional input to our behaviour.
Immutable object
110
Kuba @kuba.zalas.pl · 22/11/2024
It's the most pleasant test to write as it tends to naturally follow the Arrange/Act/Assert, a.k.a. Given/When/Then.
 Given this input
 When I invoke this behaviour
 Then this the output I expect
Implementation
100
Kuba @kuba.zalas.pl · 22/11/2024
The simplest case are tests for calculations. 
Calculations have no side effects and, called with the same set of arguments, always return the same outcome.
Test case for a calculation
110
Kuba @kuba.zalas.pl · 22/11/2024
What is a behaviour?
 Behaviour can be defined by its input and its output.
Behaviour = input + output
110
Kuba @kuba.zalas.pl · 22/11/2024
Let's start with data. How do we write a test for data?
 We don't.
 Data has no behaviour on its own.
Data
100
Kuba @kuba.zalas.pl · 22/11/2024
As software developers, we write #tests for behaviours exposed by systems we build. How to verify a behaviour?
 Let's consider a few examples in #kotlin
Behaviours all the way down
192
Kuba @kuba.zalas.pl · 15/11/2024
Usage looks similar, but we now have an explicit representation of state.
fun execute(command: GameCommand, game: Game): Either<GameError, NonEmptyList<GameEvent>> {
  if (game.isWon()) {
    return GameAlreadyWon().left()
  }
  TODO()
} 

data class Game(val guesses: List<Guess>, val outcome: Outcome) {
  fun isWon() = outcome == Outcome.WON
}
110