102 reads

Don't Mix Data Access Concerns With Essential Business Behavior

by Maximiliano ContieriMay 26th, 2025
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Passing databases creates accidental coupling and breaks business encapsulation.
featured image - Don't Mix Data Access Concerns With Essential Business Behavior
Maximiliano Contieri HackerNoon profile picture
0-item

Passing databases creates accidental coupling and breaks business encapsulation.

TL;DR: Don't mix data access concerns with essential business behavior.

Problems ๐Ÿ˜”

  • Tight Coupling
  • Mixed responsibilities
  • Bijection violation
  • Testability
  • Business logic pollution
  • Separation of concerns violation
  • Blurred Layers
  • Single Responsibility Principle violation

Solutions ๐Ÿ˜ƒ

  1. Use dependency injection
  2. Don't use the Repository Pattern. Find real abstractions instead
  3. Separate business logic
  4. Design for Decoupling

Refactorings โš™๏ธ

Context ๐Ÿ’ฌ

When you pass a database connection or database object directly to business objects, you create accidental coupling between your domain logic and data persistence mechanisms.

This approach gives you a false sensation of flexibility while making your code harder to test, maintain, and evolve.

The database becomes an implementation detail that leaks into your business layer, violating the separation of concerns principle.

Your business objects should focus on essential business rules and behavior, not on accidental logic like how data gets stored or retrieved.

This pattern also makes unit testing extremely difficult since you cannot mock or stub the database interactions without complex setup procedures.

Sample Code ๐Ÿ“–

Wrong โŒ

class InvoiceProcessor:
    def process_invoice(self, invoice_data, database):
        # Business logic mixed with database access
        customer = database.execute(
            "SELECT * FROM customers WHERE id = ?", 
            invoice_data['customer_id']
        ).fetchone()
        
        if customer['credit_limit'] < invoice_data['amount']:
            raise Exception("Credit limit exceeded")
        
        # More business logic
        tax = invoice_data['amount'] * 0.21
        total = invoice_data['amount'] + tax
        
        # Direct database manipulation
        database.execute(
            "INSERT INTO invoices (customer_id, amount, tax, total) "
            "VALUES (?, ?, ?, ?)",
            (invoice_data['customer_id'], invoice_data['amount'], 
             tax, total)
        )
        
        database.commit()
        return total

Right ๐Ÿ‘‰

class InvoiceProcessor:
    def __init__(self, billing_ledger):
        self.billing_ledger = billing_ledger
    
    def process_invoice(self, customer, amount):
        # Pure business logic with proper domain objects
        if customer.credit_limit < amount:
            raise CreditLimitExceededException()
        
        # Business calculations
        tax = amount * 0.21
        total = amount + tax
        
        # Create the domain object
        # No repositories are involved
        invoice = Invoice(
            customer=customer,
            amount=amount,
            tax=tax,
            total=total
        )
        
        self.billing_ledger.record(invoice)
        return total

Detection ๐Ÿ”

  • [x]Semi-Automatic

You can detect this smell when you find database connections, SQL queries, or ORM objects passed as parameters to business methods. Look for method signatures that accept database-related objects or when you see SQL statements mixed with business logic calculations.

Static analysis tools can flag methods that receive database connections as parameters, and code reviews should catch these architectural violations.

Exceptions ๐Ÿ›‘

  • Low level database access does not cross domain when they pass the database as argument

Tags ๐Ÿท๏ธ

  • Coupling

Level ๐Ÿ”‹

  • Intermediate

Why the Bijection Is Important ๐Ÿ—บ๏ธ

Your business objects should model real-world entities and behaviors without knowing about storage mechanisms.

When you pass databases as parameters, you break the one-to-one correspondence between business concepts and code representation.

In the real world, an invoice processor doesn't carry around a database.

it works with customers and invoices as business entities.

Breaking this bijection creates artificial dependencies that don't exist in the problem domain, making your code harder to understand and maintain.

AI Generation ๐Ÿค–

AI code generators frequently create this smell, suggesting quick solutions that directly couple database access with business logic.

They prioritize working code over clean architecture, leading to tightly coupled implementations.

AI Detection ๐Ÿฅƒ

AI tools can detect this smell when you provide clear instructions about the separation of concerns and dependency injection patterns.

Try Them! ๐Ÿ› 

Remember: AI Assistants make lots of mistakes

Suggested Prompt: Remove the coupling of the database

Without Proper Instructions

With Specific Instructions

ChatGPT

ChatGPT

Claude

Claude

Perplexity

Perplexity

Copilot

Copilot

Gemini

Gemini

DeepSeek

DeepSeek

Meta AI

Meta AI

Grok

Grok

Qwen

Qwen

Conclusion ๐Ÿ

Avoid passing databases as parameters to business objects.

This approach keeps your business logic clean, makes testing easier, and maintains proper separation between the domain and infrastructure concerns.

Relations ๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-x-i7r34uj

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-vi-cmj31om

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-vii-8dk31x0

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xiii

More Information ๐Ÿ“•

Disclaimer ๐Ÿ“˜

Code Smells are my opinion.

Credits ๐Ÿ™

Photo by Josh Appel on Unsplash


The secret to building large apps is never build large apps. Break your applications into small pieces. Then, assemble those testable, bite-sized pieces into your big application

Justin Meyer


This article is part of the CodeSmell Series.


Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks