Why another article about this? I struggled in the beginning to understand the clean architecture, so I decided to write my own explanation in a way that resembles how we learn: by building progressively refined iterative models.
I’m a fan of Xiaomi/Yeelight LEDs, especially because they can be used as smart home devices. You can control these lights with Xiaomi Home, Google Home, or Yeelights apps, but I wanted something that I could use while I was at my Windows PC so I didn’t have to look for the smartphone, unlock it, and look for the app and open it.
I imagined something in the Windows tray, which, with minimum clicks, allowed me to control the LEDs. I searched the web for a while but there was nothing. I found Yeelight Control (not working), Yeelight UWP, Yeelight…
📝 I used Kotlin in the code samples, but all the patterns apply regardless of the language or runtime.
I use frameworks and libraries when it pays off. In what concerns DI, I recommend doing it manually. Check how clear and effective vanilla DI is:
val coffeeProvider = CoffeeProvider(System.getenv("PROVIDER_URL"))
val coffeeNotifier = CoffeeNotifier()val coffeeMaker = CoffeeMaker(coffeeProvider, coffeeNotifier)
“Dependency Injection” is a 25-dollar term for a 5-cent concept. [… it] means giving an object its instance variables. Really. That’s it. Dependency Injection Demystified
DI is a design concern, not a tool. The way to achieve it is to learn its…
How do you ensure multiple apps and services of the same system play well together?
You could run end-to-end testing but it’s nearly impossible to test all the combinations. Also, the effective pinpointing of problems would be very low.
You could also test in isolation, on a per-app basis by simulating the other services' replies, but since they can evolve independently, the safety net is low as each set of tests may pass although the integration is broken.
Finally, integration testing is the typical solution because tests are run against the depended-on services. …
I assume you, developer or not, care about your backlog. Backlogs, like codebases, need and deserve care from everyone in the team. I’m a fan of continuous codebase improvement through constant refactoring. I love coding and I care a lot about all the codebases I work with. I understand migrations and upgrades need to happen, so why do I propose we stop creating technical stories? There’s a lot of harm in doing so. Let’s see why.
What are you paid for? I often hear “to write code”. I don’t agree with that at all since I believe we’re paid to…
The techniques to test the data layer can be direct, like testing against an in-memory database (e.g. H2) or against a real database (e.g. using Docker) or indirect, like launching the backend application or testing as the end-user (UI tests). Let’s go over the direct options. The presented techniques can be applied regardless of the database technology (e.g. PostgreSQL, Redis, MongoDB).
According to the hexagonal architecture, an adapter belongs to the outer layer of your app; it’s the glue between a component and the outside world, whether a database, a gateway for a REST API, your own REST API, etc. Adapters should only deal with parsing, (de)serialization, routing, error handling, and similar; they should never contain any kind of business logic.
The need to store and transmit data requires some kind of string representation which is given by serializers (object → string). Deserializers do the opposite (string → object). JSON is just a possible string representation but there are others.
The testing strategy is how you approach the automated testing of your software. Given its importance, it should be taken seriously. I’ll propose a visual way to represent it, as visual representations promote discussion, a shared understanding, and support decision making.
In a world full of technicians and politicians all having different levels of understanding, a graphic representation was often the only way to make a point; a single plummeting graphic usually aroused ten times the reaction inspired by volumes of spreadsheets. Digital Fortress
… we need to define some terminology (inspired in xUnit Test Patterns). I avoid the terms…
Creating constants can be a symptom of some underlying anti-patterns. But aren’t magic numbers (or strings, etc.) a bad practice? Well… don’t follow rules just because you always did it. Let’s address the typical arguments (we’ll use Kotlin in the snippets).
This is the worst reason to create constants. Tests should know nothing about the implementation other than its public APIs. Initially, a certain constant was local/private but you promoted it to global for the sake of testing. This was a bad idea because now the tests are coupled with the implementation. Tests should be seen as clients of the…
In an agile mindset, the team is responsible for the delivered software, which includes quality assurance (QA). In this scenario, QA is a role that is adopted by the team as a whole — developers should be writing automated tests. Manual testing does not scale as it’s hard to reproduce, tedious, error-prone, and not compatible with continuous delivery. However, if there are QA specialized people, they should be doing other types of work (e.g. exploratory testing), but I’ll leave that for another article.
Automating everything — from build to tests, deployment and infrastructure — is your only way forward. …