遵循DDD设计原则的PHP服务入口方法命名最佳实践(含命令、适配器模式)
Great question—naming is one of the most impactful (and often overlooked) parts of DDD, especially in PHP where we’re often balancing framework conventions with domain clarity. Let’s break this down with practical examples and best practices.
DDD Service Entry Point Naming Best Practices
The golden rule here is to speak the domain language. Your method names should read like business actions, not generic technical terms. Here’s what that looks like:
- Use action verbs first: Focus on what the service does for the domain. Avoid vague names like
process(),handle(), orexecute()—instead, usesubmitOrder(),cancelSubscription(),fulfillShipment(), orrefundPayment(). - Mirror domain terminology: If your stakeholders refer to "closing a support ticket" instead of "resolving it", name your method
closeSupportTicket()to keep alignment with the business. - Keep it specific: Avoid overloaded methods that do multiple things. If a service handles order submission and cancellation, split them into two distinct methods instead of a single
manageOrder().
Example in PHP:
class OrderFulfillmentService { // Clear, domain-aligned action public function fulfillShipment(ShipmentId $shipmentId): void { // Domain logic to pick, pack, and mark shipment as fulfilled } // Another distinct business action public function markShipmentAsDelivered(ShipmentId $shipmentId, DeliveryProof $proof): void { // Logic to validate proof and update shipment status } }
Alignment with Class Naming
Short answer: No, they don’t need to match—but they should complement each other.
- Service classes are named with nouns that describe their responsibility (e.g.,
OrderProcessingService,CustomerAccountService). They represent a bounded context or a specific set of related business capabilities. - Entry point methods are named with verbs that describe the specific action the service performs. The class name sets the context, the method name defines the action.
This separation makes your code self-documenting: when you see $orderService->submitOrder($order), you immediately know which service is handling the action and what it’s doing.
Command Pattern Handling
In the Command Pattern, the naming convention shifts a bit because we’re dealing with discrete, self-contained commands. Here’s how to approach it in PHP:
- Command classes: Name them as nouns that represent the action to be taken (e.g.,
SubmitOrderCommand,CancelSubscriptionCommand). Include all necessary data as readonly properties so the command is immutable. - Command Handler classes: Name them to match the command, suffixed with
Handler(e.g.,SubmitOrderCommandHandler). The entry point method is almost universally namedhandle()—this is a standard convention because each handler should have a single responsibility: processing one specific command.
Example:
// Command: Encapsulates the data needed to submit an order class SubmitOrderCommand { public function __construct( public readonly OrderId $orderId, public readonly CustomerId $customerId, public readonly array $lineItems ) {} } // Handler: Processes the command class SubmitOrderCommandHandler { public function __construct(private readonly OrderRepository $orderRepo) {} // Standard entry point method for command handlers public function handle(SubmitOrderCommand $command): void { $order = $this->orderRepo->findById($command->orderId); $order->submit($command->customerId, $command->lineItems); $this->orderRepo->save($order); } }
The handle() method works here because the handler’s class name already makes its purpose clear—there’s no ambiguity about what it’s handling.
Adapter Pattern Handling
Adapters are all about bridging incompatible interfaces, so naming depends on what you’re adapting:
- Adapting to a domain interface: If you’re wrapping a legacy service or external API to fit your domain’s interface, your adapter’s entry point methods must match the interface’s method names. This ensures consistency across your domain layer.
Example:
// Domain interface defining expected order actions interface OrderServiceInterface { public function submitOrder(Order $order): void; } // Adapter wrapping a legacy system to fit the domain interface class LegacyOrderServiceAdapter implements OrderServiceInterface { public function __construct(private readonly LegacyOrderSystem $legacySystem) {} // Matches the interface's method name, even though the legacy system uses a different name public function submitOrder(Order $order): void { $this->legacySystem->createNewOrder( $order->getId()->toString(), $order->getCustomer()->getId()->toString() ); } }
- Adapting external systems: If you’re creating an adapter for an external service (like a payment gateway), balance external terminology with your domain language. For example, if the payment gateway uses
chargeCustomer(), you could name your adapter methodchargeCustomer()for clarity, or map it to a domain-specific name likeprocessPayment()if that’s what your team uses.
Final Takeaway
At the end of the day, consistency and domain alignment are more important than rigid rules. Stick to business language wherever possible, make your method names actionable, and let patterns like Command or Adapter dictate conventions where they add clarity.
内容的提问来源于stack exchange,提问作者peterpeterson

