Table of Contents:
Magento’s Framework layer (Magento\Framework\*
) is the backbone of application services: it powers dependency injection, routing, caching, and more. But it’s not where business logic belongs.
Use the Framework layer to:
SearchCriteriaBuilder
or Filesystem
that support modular service behavior.What to avoid:
MessageManagerInterface
or Session
objects into service classes, which tightly couples logic to the UI layer.Why it matters: By depending directly on framework classes in business logic, you limit the portability of that code. It becomes harder to reuse in APIs, CLI tools, or background jobs, and test coverage suffers as a result.
Each Magento module should represent a focused, testable unit of behavior—not a grab bag of unrelated logic. When modules are scoped too broadly or entangled with each other, refactoring becomes risky and slow.
In a well-structured module:
Api/
directory, defining what the module exposes.Api/Data/
, enabling type-safe communication.di.xml
, module.xml
, and etc/config.xml
, and reflect only what’s actually used.Poorly designed modules often:
Tip for maintainability: Keep modules loosely coupled and explicitly versioned. Use sequence
in module.xml
to manage load order and ensure each module is deployable and testable on its own.
Magento’s DI container enables clean object creation, but misuse is common. Injecting too many services, relying on concrete classes, or bypassing DI entirely with ObjectManager::get()
can quickly turn a flexible system into a fragile one.
Use DI for:
Avoid DI when:
ObjectManager
to resolve dynamic dependencies (use factories or proxies instead).Patterns that help:
ProductRepository
or StockRegistry
when access is conditional.Watch out: If a class has more than 5–7 dependencies, it's a strong sign it needs to be broken down. Class responsibilities should be narrow and clearly defined.
Good Magento code separates interface from implementation—and that separation is what enables strong, maintainable tests.
Use the Api/
directory for service contracts and the @api
annotation only when those interfaces are intended for public consumption (e.g., reusable by other modules or integrations). Avoid exposing internal logic unnecessarily.
Testing tips:
Practical example: Rather than formatting a price directly in a .phtml
file or helper, inject a ViewModel that formats based on store context and uses PriceCurrencyInterface
to remain locale-aware.
Magento’s file structure is deep—but clean organization pays off in the long run.
Follow PSR-4 naming conventions:
Model/
, Service/
, ViewModel/
, Observer/
, and Plugin/
should reflect clear intent.Group logic by responsibility, not by delivery method. For example, avoid putting data fetch logic in a controller—it belongs in a service or repository.
Avoid:
ResourceModel
and Collection
patterns.Maintain readable, self-documenting code. Favor expressive class names over comments explaining behavior. And when in doubt, add a README to each module to clarify its purpose and key dependencies.
Magento 2 gives developers a powerful architecture—but it doesn’t enforce good practices out of the box. That responsibility lies with your team.
By adhering to clear boundaries, isolating business logic, and respecting DI and modularity principles, you gain more than just cleaner code—you get a codebase that enables rapid change, safe extension, and easier onboarding.
Your future developers—and your business stakeholders—will thank you for writing Magento code that scales.