Useful principle and interesting approach (see also hexagonal architecture), but long copy has generative smells such as introducing a topic with the "And topic?" pattern and the "Thing isn't just x, it's y." pattern.
I get the reaction. For something like a simple login flow, this probably looks like a lot of ceremony. The goal isn’t to over-engineer it, though. Each piece handles a single concern that may evolve independently over time (email normalization, authentication, session creation). Keeping those parts separate makes it easier to test, extend, and reuse.
And while the example is intentionally simple, the benefits show up more clearly in complex scenarios, like when workflows start branching, new auth methods are added, or the same logic needs to run in different contexts (web, API, background job, etc.). Having composable, consistent services keeps that complexity from leaking everywhere else.
It’s not the only way to structure things, but it works well for me.
3 comments
[ 8.2 ms ] story [ 25.2 ms ] threadEmailService::Normalizer strips and downcases the email UserService::Authenticator checks credentials SessionService::Creator creates a session"
Don't know how to say this without sounding arrogant. WTF?
I get the reaction. For something like a simple login flow, this probably looks like a lot of ceremony. The goal isn’t to over-engineer it, though. Each piece handles a single concern that may evolve independently over time (email normalization, authentication, session creation). Keeping those parts separate makes it easier to test, extend, and reuse.
And while the example is intentionally simple, the benefits show up more clearly in complex scenarios, like when workflows start branching, new auth methods are added, or the same logic needs to run in different contexts (web, API, background job, etc.). Having composable, consistent services keeps that complexity from leaking everywhere else.
It’s not the only way to structure things, but it works well for me.