Table of Contents
Data Access Anti-Patterns
❌ Anti-Pattern 1: Direct Model Usage Instead of Repository
Why It's Bad
- → Bypasses Service Contracts: Extensions won't intercept this operation
- → Skips Plugins: Plugins on CustomerRepositoryInterface won't execute
- → No Event Dispatch: customer_save_after_data_object event not fired
- → Breaks API Compatibility: Third-party code expecting repository pattern won't work
Bad Code Example
Bad
<?php
namespace Vendor\Module\Model;
use Magento\Customer\Model\CustomerFactory;
class CustomerService
{
private CustomerFactory $customerFactory;
public function updateCustomerName(int $customerId, string $firstname, string $lastname): void
{
// WRONG: Direct model manipulation
$customer = $this->customerFactory->create();
$customer->load($customerId);
$customer->setFirstname($firstname);
$customer->setLastname($lastname);
$customer->save();
}
}
Good Code Example
Good
<?php
declare(strict_types=1);
namespace Vendor\Module\Model;
use Magento\Customer\Api\CustomerRepositoryInterface;
class CustomerService
{
private CustomerRepositoryInterface $customerRepository;
public function updateCustomerName(int $customerId, string $firstname, string $lastname): void
{
// CORRECT: Use repository service contract
$customer = $this->customerRepository->getById($customerId);
$customer->setFirstname($firstname);
$customer->setLastname($lastname);
$this->customerRepository->save($customer);
}
}
Why It's Better
- ✓ Service Contract Compliance: Follows Magento's service contract pattern
- ✓ Plugin Support: All plugins execute correctly
- ✓ Transaction Safety: Ensures atomic save operations
- ✓ Event Dispatch: Proper event chain fires
❌ Anti-Pattern 2: Loading Full Customer When Only ID Needed
Why It's Bad
- → Performance Waste: Loads all EAV attributes (10+ SQL queries) when only 1 field needed
- → Memory Overhead: Loads entire customer object into memory
- → Slow Response: 100-500ms customer load vs. 5-10ms direct query
Correct Approach: Direct SQL Query
Performance Gain: 10-50x faster
Use ResourceConnection for single-field lookups instead of loading full customer entity.
❌ Anti-Pattern 3: Excessive Custom EAV Attributes
Why It's Bad
- → Query Complexity: Each EAV attribute = 1 LEFT JOIN (50 attributes = 50 JOINs)
- → Slow Customer Load: 2000ms+ load time with 50+ custom attributes
- → Database Stress: Complex execution plans, potential table locks
Correct Approach: Extension Attributes + Custom Table
Performance Gain: 10-100x faster
- ✓ Single JOIN: Only 1 JOIN to extended data table
- ✓ Fast Queries: 20-50ms vs. 2000ms
- ✓ Lazy Loading: Extended data only loaded when needed
Cache Anti-Patterns
❌ Anti-Pattern 4: Ignoring Customer Group Cache After Group Change
Why It's Bad
- → Stale Prices: Full Page Cache serves old pricing based on previous customer group
- → Wrong Promotions: Catalog rules don't apply immediately
- → Customer Confusion: "Why are prices still the same?"
- → Revenue Impact: Potential overcharging or undercharging
Why It's Better
- ✓ Fresh Pricing: FPC cleared, next page load shows correct prices
- ✓ Correct Business Logic: Group-based rules apply immediately
- ✓ Data Integrity: Cache state matches database state
Observer Anti-Patterns
❌ Anti-Pattern 6: Synchronous External API Calls in Observers
Why It's Bad
- → Slow Saves: Customer save operations take 200-1000ms longer
- → Failure Coupling: External API failure causes customer save to fail
- → Poor UX: Admin users wait for external API during customer edits
Performance Gain
10x faster: 51ms (with queue) vs. 500ms (synchronous API call)
Use async queue pattern for external API calls instead of blocking observer execution.
❌ Anti-Pattern 7: Bulk Operations Not Disabling Observers
Why It's Bad
- → Extremely Slow: 10,000 saves × 50ms = 500 seconds (8+ minutes)
- → Unnecessary Work: Email sync, audit logs not needed for bulk operations
- → Timeout Risk: Script may hit max_execution_time
Performance Gain
1000x faster: 0.5 seconds vs. 500 seconds for 10,000 customers
Use direct SQL UPDATE for bulk operations instead of looping through repository saves.
Quick Reference Summary
| Anti-Pattern | Impact | Correct Approach |
|---|---|---|
| Direct model usage | Bypasses plugins, events | Use repository interface |
| Loading full customer for one field | 10-50x slower | Direct SQL query for field |
| Excessive EAV attributes | 2000ms+ load time | Extension attributes + custom table |
| Sync external API in observer | 10-100x slower saves | Async queue pattern |
| Bulk ops with observers | 25x slower | Disable observers or direct SQL |