Live
Black Hat USAAI BusinessBlack Hat AsiaAI BusinessDoes GPT-2 Have a Fear Direction?lesswrong.comY Combinator's CEO says he ships 37,000 lines of AI code per dayHacker News AI TopShow HN: SpeechSDK – free, open-source SDK that unifies all AI voice modelsHacker News AI TopWe Ditched LangChain. Here’s What We Built Instead — and Why It’s Better for Serious AI Research.Medium AIAMD vs. Nvidia: The AI Supercycle Is Big Enough for Both. Here's the Better Buy. - AOL.comGNews AI NVIDIAI Broke Up With ChatGPT (And My Productivity Thanked Me)Medium AIAI startup envisions '100M new people' making videogamesHacker News AI TopMost Students Think ChatGPT Helps Them Study — Here’s Why It Actually Slows Them Down (And How to…Medium AIWhen the server crashes the soulMedium AIDeepfakes and malware: AI menu grows longer for threat actors, causing headaches for defenders - SiliconANGLEGNews AI deepfakeAMD vs. Nvidia: The AI Supercycle Is Big Enough for Both. Here's the Better Buy. - The Motley FoolGNews AI NVIDIAThe AI That Refuses to Advise, And Why That Changes EverythingMedium AIBlack Hat USAAI BusinessBlack Hat AsiaAI BusinessDoes GPT-2 Have a Fear Direction?lesswrong.comY Combinator's CEO says he ships 37,000 lines of AI code per dayHacker News AI TopShow HN: SpeechSDK – free, open-source SDK that unifies all AI voice modelsHacker News AI TopWe Ditched LangChain. Here’s What We Built Instead — and Why It’s Better for Serious AI Research.Medium AIAMD vs. Nvidia: The AI Supercycle Is Big Enough for Both. Here's the Better Buy. - AOL.comGNews AI NVIDIAI Broke Up With ChatGPT (And My Productivity Thanked Me)Medium AIAI startup envisions '100M new people' making videogamesHacker News AI TopMost Students Think ChatGPT Helps Them Study — Here’s Why It Actually Slows Them Down (And How to…Medium AIWhen the server crashes the soulMedium AIDeepfakes and malware: AI menu grows longer for threat actors, causing headaches for defenders - SiliconANGLEGNews AI deepfakeAMD vs. Nvidia: The AI Supercycle Is Big Enough for Both. Here's the Better Buy. - The Motley FoolGNews AI NVIDIAThe AI That Refuses to Advise, And Why That Changes EverythingMedium AI
AI NEWS HUBbyEIGENVECTOREigenvector

There Is No Such Thing As a Service

DEV Communityby ThatGhostApril 1, 20265 min read1 views
Source Quiz

<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

/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)  // ... }

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) }

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() }

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) }

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.

Was this article helpful?

Sign in to highlight and annotate this article

AI
Ask AI about this article
Powered by Eigenvector · full article context loaded
Ready

Conversation starters

Ask anything about this article…

Daily AI Digest

Get the top 5 AI stories delivered to your inbox every morning.

More about

modeltransformerversion

Knowledge Map

Knowledge Map
TopicsEntitiesSource
There Is No…modeltransformerversionupdateservicefeatureDEV Communi…

Connected Articles — Knowledge Graph

This article is connected to other articles through shared AI topics and tags.

Knowledge Graph100 articles · 129 connections
Scroll to zoom · drag to pan · click to open

Discussion

Sign in to join the discussion

No comments yet — be the first to share your thoughts!

More in Releases