Why “UserService” Is a Bad Name: Lessons from a 12,000‑Line Service
The article recounts a 12,000‑line UserService.java file, explains how the overly generic name masks multiple responsibilities, violates the single‑responsibility principle, and creates a distributed monolith, then shows how domain‑driven naming and bounded‑context splitting can restore clear architecture.
Symptom: a 12,000‑line UserService.java
During a night‑time code review the author discovered a file named UserService.java that had grown to 12,000 lines. The file started in 2018 as a simple user‑related service and gradually accumulated login, OAuth, real‑name verification, membership, and permission logic as new features were added.
Why the name is the root cause
The generic name UserService gives the illusion that any user‑related concern can be placed there. It reflects a data‑oriented, CRUD mindset rather than a domain‑oriented one, exposing the underlying user table instead of a specific business capability. This violates the Single Responsibility Principle and forces three different business teams—operations, security, and product—to edit the same file.
Same “User” means different things in different contexts
In the Authentication context, a User is a credential holder; in the Profile context, a User is a social graph; in the Membership context, a User is a paid‑member account. The author cites a code‑review incident where a developer worried that a change to the login logic would affect membership calculations, illustrating the confusion caused by the monolithic name.
Naming anti‑patterns
The article identifies two common anti‑patterns: data‑oriented naming (e.g., UserService, OrderService) that signals CRUD thinking, and process‑oriented naming (e.g., UserProcessService, UserHandler) that treats business logic as a script rather than a domain model. It argues that a good name should convey a clear business capability and bounded context.
How to split the monolith
The author proposes vertical decomposition based on bounded contexts: IdentityService (login, registration, OAuth, real‑name verification), ProfileService (nickname, avatar, privacy), PermissionService (roles, resource checks), and MembershipService (levels, points, benefits). Interaction between these services should occur via domain events or anti‑corruption layers. For example, IdentityService defines a MembershipProvider interface in the domain layer, while the infrastructure layer implements it with an RPC client to MembershipService, keeping each service’s responsibilities isolated.
After the split, each team can evolve its service independently—IdentityService can adjust login policies without touching membership logic, ProfileService can refactor avatar storage without affecting authentication, and MembershipService can upgrade its points algorithm without coordinating releases with other teams. The ultimate goal is to keep the right business logic inside the right boundary.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Infinite Tech Management
13 years in technology, 6 years in management, experience at multiple top firms; documenting real pitfalls and growth of tech managers, focusing on both tech management and architecture, and pursuing dual development in these areas.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
