Keep DTOs Thin — Move Logic to Services

📖 1 minute read

When building API integrations, it’s tempting to put helper methods on your DTOs. I learned this creates problems.

Started with “fat” DTOs:

class Product {
    public function getUniqueVariants() { ... }
    public function isVariantAvailable($type) { ... }
    public function getDisplayType() { ... }
}

Seemed convenient — call $product->getUniqueVariants() anywhere. But issues emerged:

  1. Serialization problems: DTOs with methods are harder to cache/serialize
  2. Testing complexity: need to mock entire DTO structures just to test one method
  3. Coupling: business logic tied to the external API’s data structure

The fix — move all logic to a service class:

class ApiClient {
    public function getUniqueVariants(Product $product) { ... }
    public function isVariantAvailable(Product $product, string $type) { ... }
}

Now DTOs are pure data containers — just public properties or readonly classes. Business logic lives in services where it belongs.

Feels less convenient at first (passing DTOs as arguments), but far more maintainable. DTOs stay simple and serializable. Services become testable in isolation.

Daryle De Silva

VP of Technology

11+ years building and scaling web applications. Writing about what I learn in the trenches.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *