惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
aimingoo的专栏
aimingoo的专栏
IT之家
IT之家
N
Netflix TechBlog - Medium
MyScale Blog
MyScale Blog
雷峰网
雷峰网
T
Tailwind CSS Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
The Blog of Author Tim Ferriss
S
Schneier on Security
C
CERT Recently Published Vulnerability Notes
Help Net Security
Help Net Security
云风的 BLOG
云风的 BLOG
GbyAI
GbyAI
I
InfoQ
H
Help Net Security
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
酷 壳 – CoolShell
酷 壳 – CoolShell
G
GRAHAM CLULEY
Blog — PlanetScale
Blog — PlanetScale
G
Google Developers Blog
I
Intezer
大猫的无限游戏
大猫的无限游戏
AWS News Blog
AWS News Blog
Recent Announcements
Recent Announcements
Google DeepMind News
Google DeepMind News
Spread Privacy
Spread Privacy
博客园_首页
宝玉的分享
宝玉的分享
量子位
T
Threatpost
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Security Latest
Security Latest
C
Cybersecurity and Infrastructure Security Agency CISA
SecWiki News
SecWiki News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - Franky
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
The Exploit Database - CXSecurity.com
T
Tenable Blog
Know Your Adversary
Know Your Adversary
P
Proofpoint News Feed
The Register - Security
The Register - Security
V2EX - 技术
V2EX - 技术
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Last Week in AI
Last Week in AI
L
LangChain Blog
T
Tor Project blog
Stack Overflow Blog
Stack Overflow Blog
月光博客
月光博客

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Reducing Complexity: Replacing Entity-Based Services and Repositories with Purposeful Layers in Software Design
Artyom Kornilov · 2026-06-26 · via DEV Community

Introduction: The Prevalence of Entity-Based Patterns

Walk into any mid-sized software project today, and you’ll find the same architectural blueprint repeated ad nauseam: UserService, UserRepository, OrderService, OrderRepository. It’s become the default blueprint, a reflexive response to the question, “How do we structure this?” The pattern itself isn’t inherently flawed—layering is a fundamental principle of software design. But the mindless application of entity-based Services and Repositories is where the system starts to deform under its own weight.

Let’s break down the mechanism of this failure. In theory, a Service is supposed to encapsulate meaningful application behavior—interactions with payment gateways, email systems, or external APIs. A Repository, meanwhile, should abstract complex data access logic: multi-source queries, caching strategies, or evolving storage mechanisms. These layers are meant to isolate complexity, not create it. But in practice, they often function as empty shells:

  • Services degrade into mere delegates, forwarding calls without adding value. Example: a UserService that does nothing but call userRepository.save(user).
  • Repositories become thin wrappers around an ORM, adding no logic beyond what the ORM already provides. Example: a UserRepository with methods like findById(id) that directly map to JPA/Hibernate calls.
  • Interfaces are introduced without meaningful alternative implementations, turning them into ceremonial artifacts. Example: a UserRepository interface with a single Hibernate-backed implementation, never intended for mocking or polymorphism.

The causal chain here is straightforward: impact → internal process → observable effect. When these layers lack purpose, they introduce indirection without abstraction. Each additional layer adds cognitive load, file clutter, and potential failure points. The system doesn’t become more maintainable—it becomes harder to trace. Developers spend more time navigating boilerplate than solving actual problems. Over time, the codebase expands horizontally (more files, more classes) without deepening vertically (more meaningful logic per component).

Consider the risk mechanism: if a junior developer joins the project, they’ll likely replicate the existing pattern, compounding the issue. The architecture becomes self-perpetuating, not because it’s optimal, but because it’s familiar. This is where the pattern stops working: when it’s applied as a template, not a tool. The optimal solution? Design around intent, not structure.

For example, instead of a generic UserService, create a RegisterNewUser class that explicitly handles the use case. Instead of a UserRepository, use the ORM directly if there’s no complex data logic to abstract. The rule is simple: if the layer doesn’t isolate complexity, eliminate it. This approach reduces noise, aligns code with system behavior, and prevents the codebase from becoming a labyrinth of redundant abstractions.

The typical choice error here is over-abstraction: developers default to patterns because they fear under-engineering. But the real risk isn’t missing a layer—it’s adding one that doesn’t pay rent. The system breaks when layers accumulate without purpose, not when they’re omitted where unnecessary.

The Problem: Layers Without Purpose

Every day, I see projects where the layer-per-entity dogma reigns supreme: UserService, UserRepository, OrderService, OrderRepository, and so on. The issue isn’t layering itself—it’s how these layers are misapplied. Let’s dissect the mechanism of failure.

1. The Degradation of Services and Repositories

In theory, a Service should encapsulate meaningful application behavior—interactions with external systems, complex business logic, or significant use cases. A Repository should handle non-trivial data access: multiple data sources, caching strategies, or evolving persistence mechanisms.

In practice, they often deform into something far less useful:

  • Services as Delegates: A UserService that does nothing but call userRepository.save(user). No logic, no abstraction—just indirection that adds cognitive load without value.
  • Repositories as ORM Wrappers: A UserRepository that maps findById(id) directly to JPA/Hibernate. No complexity isolation, no additional logic—just file clutter.
  • Ceremonial Interfaces: Interfaces introduced without alternative implementations. They don’t decouple anything—they just expand the codebase horizontally without deepening it vertically.

2. The Causal Chain of Failure

Here’s how this pattern breaks software architecture:

  • Impact: Layers without purpose introduce indirection without abstraction.
  • Internal Process: Each unnecessary layer adds a failure point—a place where bugs can hide, where changes require coordination, and where developers must mentally navigate.
  • Observable Effect: The codebase expands horizontally (more files/classes) without deepening vertically (meaningful logic per component). Maintainability deteriorates, and scalability becomes a friction point.

3. The Risk Mechanism

Why does this happen? The risk forms through a combination of:

  • Familiarity Over Optimization: Junior developers replicate patterns they’ve seen, perpetuating suboptimal architecture because it feels safe.
  • Fear of Under-Engineering: Developers add layers out of fear of missing something, but over-abstraction is the typical error. Unnecessary layers heat up the codebase—they introduce complexity without isolating it.
  • Structural Inertia: Teams default to templates (e.g., layer-per-entity) without questioning their fit for the problem. The codebase becomes a mechanical system with too many gears—each one adding friction without contributing to motion.

4. Edge-Case Analysis: When Layers Justify Their Existence

Layers are not inherently bad. They justify their existence when they isolate complexity. For example:

  • A Service that orchestrates a payment gateway, sends emails, and updates multiple aggregates—this isolates cross-cutting concerns.
  • A Repository that handles sharded databases, caching, and audit logging—this isolates data access complexity.

But if the complexity isn’t there, the layer becomes a dead weight—a component that expands the system without contributing to its function.

5. Optimal Solution: Design Around Intent, Not Structure

The optimal solution is to eliminate layers that don’t isolate complexity. Here’s the rule:

  • If X (a layer doesn’t encapsulate meaningful logic or isolate complexity) → Use Y (remove it or replace it with something purpose-driven).

For example:

  • Replace generic UserService with RegisterNewUser or ProcessPayment—classes named after concrete actions, not entities.
  • Use the ORM directly if there’s no complex data logic to hide. Don’t introduce a Repository just because it’s in the blueprint.

6. Typical Choice Errors and Their Mechanism

Developers often make these mistakes:

  • Over-Engineering: Adding layers out of fear of under-engineering. Mechanism: Fear-driven design leads to unnecessary abstraction, which breaks maintainability.
  • Template Blindness: Applying structural templates without evaluating fit. Mechanism: Inertia leads to horizontal expansion without vertical depth, deforming the codebase into a bloated, hard-to-navigate mess.

7. Professional Judgment

Layers must justify their existence by isolating complexity. If they don’t, they’re noise. The codebase should reflect what the system does, not just how it’s layered. This isn’t about avoiding patterns—it’s about applying them intentionally. If a layer doesn’t serve a purpose, it’s a mechanical failure in your architecture—remove it before it heats up your system.

Scenarios and Alternatives: Rethinking Entity-Based Patterns

The default use of entity-based Services and Repositories often leads to architectural bloat. Below are six scenarios where these patterns fall short, paired with alternatives that simplify and clarify your codebase.

1. The Delegating Service: When a Layer Becomes a Middleman

Scenario: A UserService that does nothing but delegate calls to a UserRepository. For example:

  • UserService.saveUser(user) → userRepository.save(user)
  • UserService.getUserById(id) → userRepository.findById(id)

Mechanism of Failure: The Service layer adds indirection without abstraction. Each call introduces a failure point (e.g., method signature mismatch, null handling) and requires mental navigation between files. The codebase expands horizontally (more files) without deepening vertically (meaningful logic per component).

Alternative: Eliminate the Service layer if it doesn’t orchestrate cross-cutting concerns. Use the Repository directly or replace it with action-based classes (e.g., RegisterNewUser) that encapsulate intent.

Rule: If a Service only delegates calls, remove it. Layers must isolate complexity, not just pass it along.

2. The ORM Wrapper Repository: When Abstraction Adds Noise

Scenario: A UserRepository that thinly wraps an ORM. For example:

  • UserRepository.findById(id) → entityManager.find(User.class, id)
  • UserRepository.save(user) → entityManager.persist(user)

Mechanism of Failure: The Repository layer becomes ceremonial, adding files and indirection without hiding complexity. Developers must navigate between the Repository and the ORM, increasing cognitive load. If the ORM changes, every Repository method must be updated, breaking encapsulation.

Alternative: Use the ORM directly if there’s no complex data logic. If complexity arises (e.g., sharded databases, caching), justify the Repository by isolating that logic.

Rule: If a Repository is an ORM wrapper, remove it. Abstraction must hide complexity, not just rename it.

3. Ceremonial Interfaces: When Contracts Lack Purpose

Scenario: Introducing interfaces like IUserService or IUserRepository without alternative implementations. For example:

  • IUserService → UserService (no mock, no alternate implementation)

Mechanism of Failure: Interfaces without alternatives become ceremonial, expanding the codebase horizontally without enabling flexibility. They force developers to navigate between interface and implementation, adding friction without benefit.

Alternative: Only introduce interfaces when there’s a clear need for multiple implementations (e.g., mocking, alternate data sources). Otherwise, use concrete classes directly.

Rule: If an interface has no alternative implementation, remove it. Contracts must enable flexibility, not just add files.

4. Layer-Per-Entity: When Structure Overrides Intent

Scenario: Creating UserService, OrderService, etc., for every entity, regardless of use case. For example:

  • UserService handles user registration, login, and profile updates in separate methods.

Mechanism of Failure: The codebase becomes fragmented, with related logic scattered across entity-based classes. Developers must navigate multiple files to understand a single use case (e.g., registration involves UserService, EmailService, and UserRepository).

Alternative: Organize code around use cases, not entities. For example, replace UserService.register(user) with RegisterNewUser.execute(user), encapsulating all related logic in one place.

Rule: If logic is tied to a use case, not an entity, structure it accordingly. Code should reflect intent, not structural templates.

5. Over-Engineering for Future Complexity

Scenario: Adding layers “just in case” complexity arises later. For example:

  • Creating a UserRepository for a simple CRUD application with no plans for sharded databases or caching.

Mechanism of Failure: Fear-driven design leads to over-abstraction. Each layer adds cognitive load, file clutter, and potential failure points. The codebase becomes harder to maintain as developers must navigate unnecessary indirection.

Alternative: Design for current needs, not hypothetical futures. Add layers only when complexity justifies them. For example, start with direct ORM usage and introduce a Repository if data access becomes complex.

Rule: If complexity doesn’t exist, don’t engineer for it. Layers must justify their existence today, not tomorrow.

6. Junior Developer Replication: When Familiarity Overrides Intent

Scenario: Junior developers replicate entity-based patterns from previous projects without evaluating their fit. For example:

  • Creating UserService and UserRepository because “that’s how it’s done.”

Mechanism of Failure: Familiarity over optimization leads to suboptimal architecture. Patterns are applied without understanding their purpose, resulting in layers that lack intent. The codebase becomes bloated, and maintainability suffers as developers must navigate unnecessary complexity.

Alternative: Encourage critical evaluation of patterns. Educate developers on the purpose of layers and when to use them. Foster a culture of intentional design, not template replication.

Rule: If a pattern is applied by default, question its purpose. Layers must align with system behavior, not just familiarity.

Professional Judgment: Layers Must Justify Their Existence

The core issue with entity-based Services and Repositories is not their existence but their misapplication. Layers are valid when they isolate complexity—orchestrating cross-cutting concerns, handling non-trivial data access, or enabling flexibility. However, when they degrade into delegates, wrappers, or ceremonial interfaces, they become noise.

Optimal Solution: Design around intent, not structure. Replace entity-based classes with action-based classes when logic ties to use cases, not entities. Eliminate layers that don’t isolate complexity. Use ORMs directly if no complex data logic is needed.

Typical Errors:

  • Over-Engineering: Fear of under-engineering leads to bloated codebases.
  • Template Blindness: Defaulting to structural templates without evaluating fit.

Rule of Thumb: If a layer doesn’t isolate complexity, remove it. Code should reflect system functionality, not just structure. Apply patterns intentionally, not habitually.

Conclusion: Rethinking Architectural Defaults

The dogma of entity-based Services and Repositories has become a default in software projects, but it’s time to question its value. Every day, I see projects cluttered with layers like UserService, UserRepository, OrderService, and OrderRepository. The problem isn’t layering itself—it’s how these layers are misapplied, often lacking any real purpose beyond structural convention.

The Mechanism of Failure

Let’s break it down. In theory, a Service should encapsulate meaningful application behavior—orchestrating complex workflows, interacting with external systems, or implementing business logic. A Repository should abstract data access complexity, handling sharding, caching, or multi-source persistence. But in practice:

  • Services degrade into delegates. A UserService often does nothing but call userRepository.save(user), adding a layer of indirection without abstraction. This introduces a failure point—if the method signature changes, both layers must be updated. The codebase expands horizontally (more files) without deepening vertically (meaningful logic).
  • Repositories become ORM wrappers. A UserRepository might simply map findById(id) to JPA/Hibernate. If the ORM changes, every Repository method must be revised. The abstraction doesn’t hide complexity—it just renames it, adding cognitive load and file clutter.
  • Interfaces become ceremonial. Interfaces are introduced without alternative implementations, forcing developers to navigate unnecessary contracts. This adds friction without enabling flexibility, as seen in mocking or alternate data sources.

The Causal Chain

The impact is clear: indirection without abstraction. Each layer adds a failure point, requiring coordination and mental navigation. The codebase expands horizontally, but the logic remains shallow. Maintainability suffers as developers must trace through layers that don’t isolate complexity—they just pass it along.

The Risk Mechanism

Why does this happen? It’s a combination of familiarity over optimization and fear of under-engineering. Junior developers replicate patterns they’ve seen, perpetuating suboptimal architecture. Senior developers, fearing they might miss something, over-abstract, adding layers “just in case.” The result? A bloated codebase that’s harder to understand and maintain.

The Optimal Solution

Layers must justify their existence by isolating complexity. If they don’t, remove them. Here’s how:

  • Replace delegating Services with action-based classes. Instead of UserService.register(user), use RegisterNewUser.execute(user). This encapsulates logic in one place, reflecting intent, not structure. Rule: If a Service doesn’t orchestrate cross-cutting concerns, eliminate it.
  • Use ORMs directly unless complex data logic is needed. If there’s no sharding, caching, or multi-source persistence, a Repository is unnecessary. Rule: If a Repository doesn’t hide complexity, remove it.
  • Introduce interfaces only when multiple implementations are needed. Otherwise, they’re just noise. Rule: If there’s no alternative implementation, remove the interface.

Professional Judgment

The key is to design around intent, not structure. Code should reflect what the system does, not how it’s layered. For example, instead of organizing around User and Order, structure logic by use cases like ProcessPayment or SendConfirmationEmail. This aligns the codebase with system behavior, reducing noise and improving clarity.

Typical errors include over-engineering for hypothetical complexity and template blindness. Avoid these by critically evaluating each layer’s purpose. Rule of thumb: If a layer doesn’t isolate complexity, it’s not justified.

In conclusion, entity-based Services and Repositories aren’t inherently bad—they’re just often misapplied. By rethinking defaults and designing with intent, we can create codebases that are clearer, more maintainable, and aligned with real system behavior. It’s time to stop layering for the sake of layering and start building with purpose.