Hacker News new | ask | show | jobs
by BlackFly 638 days ago
This sort of response on second thought seems like a knee-jerk, but in the off chance that you might be open to seeing the perspective that values a hexagonal architecture.

You always have two concrete implementations: the production application and the testing application. Otherwise you yolo things into prod, or run only manual/integration tests. That can work for a while, but many people find it unsavory.

It is pretty easy and sometimes useful to make three implementations: http server, CLI, test. Maybe you want to use files in CLI and a db for a server.

It has always been a good idea to isolate persistence and transport concerns from the business logic and that doesn't change in Rust. Don't dependency drill SQLite up and down every call stack. If your application is small enough and will stay so, then separating it is more a question of habit than anything.

But you shouldn't abstract everything nor try to separate everything! It was a persistence layer before, in the hexagonal architecture it becomes adapter implementations of a port. Transport layer is similar. Separation of concerns in this case means that you have a concrete dependency (an http server, a db etc) that isn't part of your logic.

2 comments

The best way to conceptualize hexagonal is as a kind of crutch to accomodate the inability of unit tests to effectively fake stuff like the db and their tendency to tightly couple to everything.

It's not intrinsically good design but it does improve unit testability (which sometimes has value and sometimes has zero value).

I am partial to property testing logic and integration testing servers. This frequently requires some level of separation, the key is to do it only at the right points.

Don't start by saying how can I unit test this tiny bit of logic against several mocks, start with a simple integration test of your real routes.

As you add abstractions you are trading maintainable straightforward code for more granular testing. It's a hard trade off not a set of principles for good code.

Not once did I mention unit testing.

What I mentioned was an adapter and port. If you can only run against a postgres database, your integration test is going to require setup and be slow. If you can easily swap the postgres adapter out for sqlite or in memory, the same test will be practically instantaneous and self contained. The same test can be occasionally run against a postgres database (like once a night), to ensure there are no postgres specific idiosyncracies.

Thus I started by saying how you can integration test quickly without mocks (sometimes it would be called a fake, but using a different db is something else) on real routes.