There Is No Such Thing As a Service
<p>If you have been following this series, you know I am a fan of services. Dependency injection, single responsibility, clean boundaries between concerns. Done right, you end up with hundreds of services, each doing exactly one thing.</p> <p>But here is the problem nobody talks about: the word "service" does not actually mean anything.</p> <p><code>ItemService</code>. What does it do? Everything. What is inside? Who knows. You have to open it and start reading. And the more your codebase grows, the more that class becomes a dumping ground, a god class disguised by a reasonable name.</p> <p>I want to argue that the service as we know it is just one of many distinct types of classes we could be writing. And the moment you start thinking in terms of those types, your code becomes something a
If you have been following this series, you know I am a fan of services. Dependency injection, single responsibility, clean boundaries between concerns. Done right, you end up with hundreds of services, each doing exactly one thing.
But here is the problem nobody talks about: the word "service" does not actually mean anything.
ItemService. What does it do? Everything. What is inside? Who knows. You have to open it and start reading. And the more your codebase grows, the more that class becomes a dumping ground, a god class disguised by a reasonable name.
I want to argue that the service as we know it is just one of many distinct types of classes we could be writing. And the moment you start thinking in terms of those types, your code becomes something anyone can navigate on instinct alone.
The Problem With "Service"
When a codebase is healthy, your services are focused. Something like:
/Users UserService AuthenticationService AuthorizationService TokenService/Users UserService AuthenticationService AuthorizationService TokenService/Items ItemService ItemRepository ItemCartService`
Enter fullscreen mode
Exit fullscreen mode
This looks great. But zoom in on ItemService six months later and you will likely find something like this:
public class ItemService { GetItem(int id) GetItems() GetItemsByCategory(int categoryId) GetItemsByName(string name) GetLatestItems() UpdateItem(Item item) UpdateItems(IEnumerable items) DeleteItem(int id) DeleteItems(int[] ids) InsertItem(Item item) InsertMany(IEnumerable items) UpsertItem(Item item) // ... }public class ItemService { GetItem(int id) GetItems() GetItemsByCategory(int categoryId) GetItemsByName(string name) GetLatestItems() UpdateItem(Item item) UpdateItems(IEnumerable items) DeleteItem(int id) DeleteItems(int[] ids) InsertItem(Item item) InsertMany(IEnumerable items) UpsertItem(Item item) // ... }Enter fullscreen mode
Exit fullscreen mode
That is not a service. That is a warehouse. It has read logic, write logic, bulk operations, filtering strategies, all crammed under one roof because "it is about items, so it goes in ItemService."
The class is technically focused on one entity. But it is not focused on one responsibility.
Rethinking the Taxonomy
Here is the shift I am proposing: stop thinking of a service as a place where you house logic. Start thinking of it as a datatype.
When you see int, you do not need documentation. You know it is a whole number. You know what operations make sense on it. The name carries the contract. We should hold our classes to the same standard. When you see ItemProvider, you should know just as instinctively what it contains and what it does, without opening the file, without reading a single method signature.
Instead of having one service per entity, you define services by what they do. The name of the class should tell you exactly what is inside before you ever open the file.
The Service
The classic. It still has its place, especially for simpler entities or flows that do not warrant a full split. A proper service handles coordinated logic for an entity, but keeps its surface area small.
public class ItemService { GetItem(int id) GetItems() InsertItem(Item item) DeleteItem(int id) }public class ItemService { GetItem(int id) GetItems() InsertItem(Item item) DeleteItem(int id) }Enter fullscreen mode
Exit fullscreen mode
Simple, stable, predictable. If it starts growing, that is your signal to promote some of its responsibilities into more specialized types.
The Provider
A Provider is a read-only class. Every method on it is a query. Nothing writes, nothing mutates. If you need data about an entity, you go to its Provider.
public class ItemProvider { GetOne(int id) GetMany() GetByCategory(int categoryId) GetByName(string name) GetLatest(int count) GetFeatured() }public class ItemProvider { GetOne(int id) GetMany() GetByCategory(int categoryId) GetByName(string name) GetLatest(int count) GetFeatured() }Enter fullscreen mode
Exit fullscreen mode
You do not need to read the method bodies. You do not need to wonder whether GetByName has a side effect somewhere. It is a Provider. It gets things. That is the entire contract.
The Mutator
The Mutator is the other half of that split. It owns all write operations. Inserts, updates, deletes, upserts. If data is changing, it goes through the Mutator.
public class ItemMutator { Insert(Item item) InsertMany(IEnumerable items) Update(Item item) UpdateMany(IEnumerable items) Upsert(Item item) UpsertMany(IEnumerable items) Delete(int id) DeleteMany(int[] ids) }public class ItemMutator { Insert(Item item) InsertMany(IEnumerable items) Update(Item item) UpdateMany(IEnumerable items) Upsert(Item item) UpsertMany(IEnumerable items) Delete(int id) DeleteMany(int[] ids) }Enter fullscreen mode
Exit fullscreen mode
Notice what this does to readability. If you see an ItemMutator being injected somewhere, you immediately know that code is writing data. If you see an ItemProvider, you know it is only reading. Your call sites become self-documenting.
You Already Do This
This is not a new concept. You already apply it to specialized cases without thinking about it.
JobHandler. EmailSender. PaymentProcessor. You already recognized those as distinct enough to deserve their own naming convention. You did not call them JobService or EmailService because the name would have been too vague.
The argument is simple: apply that same instinct to the general stuff too.
A few other types that naturally emerge:
-
Verifiers handle validation and state checks. Does this entity exist? Is this transition allowed? Is the user eligible?
-
Transformers map one object to another. DTOs to domain models, API responses to internal types, one version of a POCO to another.
You probably already have logic like this scattered across your services. Pulling it into named types makes it findable and reusable.
Why This Matters
The benefit is not just organization. It is communication.
When a new developer joins and sees ItemProvider, they know where to look for queries. When you are reviewing a pull request and see an ItemMutator injected into a controller, you immediately know that endpoint writes data. You did not open a file. You did not read a method signature. The name told you.
That is the standard we already hold primitive types to. Nobody reads the documentation for bool. Nobody opens the definition of int to understand what operations make sense on it. The name carries the contract, and the contract is never violated.
Your service layer can work the same way. Name the type, keep the class honest to that name, and the codebase stops being something people navigate and starts being something people read.
Sign in to highlight and annotate this article

Conversation starters
Daily AI Digest
Get the top 5 AI stories delivered to your inbox every morning.
More about
modeltransformerversion
A Tale of Two Rigours
A familiarity with the pre-rigor/post-rigor ontology might be helpful for reading this post. University math is often sold to students as imbuing in them the spirit of rigor and respect for iron-clad truth. The value in a real analysis course comes not from the specific results that it teaches — those are largely known to scientifically literate students by the time they take it. Instead, they are asked to relearn all those things from first principles; in so doing, they strip themselves of bad habits they previously learned and are inducted into the skeptical culture of the mathematician. Pedagogical and exam materials usually support this goal, putting emphasis on proof-writing, careful argumentation and attention to detail. This incentivises the student to cultivate an invaluable attitu

Does GPT-2 Have a Fear Direction?
Anthropic dropped a paper this morning showing that Claude Sonnet 4.5 has steerable emotion representations. Actual directions in activation space that, when injected, shift the model's behavior in predictable ways. They found a non-monotonic anger flip: push the steering vector hard enough and the model will flip to something qualitatively different than anger. The paper only covered their very large, heavily instruction tuned model. This paper is a write-up on the same same experiment at a tiny scale. The Setup: I generated 40 situational prompt pairs to extract a fewer direction via difference-in-means. No emotional words for the prompts and the contrast is entirely situational. Ex: standing at the edge of a rooftop versus standing at the edge of a meadow, alone in a parking garage at m

Two Theories for Cryopreservation
Why cryonics, and the two main methods, with practical discussion and philosophical musings on both. Epistemic status: Cryonics is a scientific field that is long established, yet long underfunded, and uncertain. I’ve been thinking about this on and off for a few years and remain cautiously optimistic. Most people who have ever lived, over 90%, have died, and most information we may need to be able to revive them has also gone. We still live in the era where a single accident or disease can swiftly and permanently end your experience of life. If you value your life, and want to continue to live indefinitely, cryogenic preservation of your body is an obvious thing to consider. Here, I will mostly talk about the two main methods of cryopreservation, with some high-level technical explanation
Knowledge Map
Connected Articles — Knowledge Graph
This article is connected to other articles through shared AI topics and tags.
More in Releases

Two Theories for Cryopreservation
Why cryonics, and the two main methods, with practical discussion and philosophical musings on both. Epistemic status: Cryonics is a scientific field that is long established, yet long underfunded, and uncertain. I’ve been thinking about this on and off for a few years and remain cautiously optimistic. Most people who have ever lived, over 90%, have died, and most information we may need to be able to revive them has also gone. We still live in the era where a single accident or disease can swiftly and permanently end your experience of life. If you value your life, and want to continue to live indefinitely, cryogenic preservation of your body is an obvious thing to consider. Here, I will mostly talk about the two main methods of cryopreservation, with some high-level technical explanation

Governor Hochul Announces AI Platform Clay to Expand New York City Headquarters, Creating Nearly 500 High-Paying Jobs | Empire State Development - esd.ny.gov
Governor Hochul Announces AI Platform Clay to Expand New York City Headquarters, Creating Nearly 500 High-Paying Jobs | Empire State Development esd.ny.gov




Discussion
Sign in to join the discussion
No comments yet — be the first to share your thoughts!