Playbook

The Playbook: From Vision to Version 1.0

Phase 1: The Blueprint (Step 1 & 2)

Before a single line of code is written, we align the business logic with the technical path.

  • Module Discovery: We draft a detailed Requirement Document for the specific feature. This ensures we are solving the right problem.
  • Logic Mapping: We translate requirements into a Visual Flow Chart. This acts as the “final contract” of logic. You see exactly how the data flows before we build the pipes.

Phase 2: The Architecture (Step 3)

We don’t believe in “code everywhere.” We follow a strict Modular Component Architecture. Each feature is treated as a self-contained unit:

  • Request & Resource: Strict validation on data entry and consistent formatting on data exit.
  • Actions: The “Fixed Spot” for business logic. Every task has one class and one purpose.
  • Services: Dedicated handlers for 3rd-party integrations and complex external utilities.
  • Models & Controllers: Kept lean and “boring,” focused only on data relationships and routing.

Phase 3: The Deployment

We move from local development to the world.

  • Environment: Automated testing and staging environments.
  • Every new Action have at least one Feature Test (Testing the “User Flow”) that proves it works.
  • Unit tests focus on a single piece of logic in total isolation. They do not touch the database, the internet, or the filesystem.
  • Infrastructure: Scalable deployment via Laravel Cloud, ensuring your app stays fast even as your user base grows.

Working with Events & Listeners

The Real-World Analogy: The Doorbell

  • The Event (The Trigger): Someone presses the doorbell (OrderPlaced).
  • The Listeners (The Reactions):
    • Listener 1: The chime rings in the hallway (User gets an email).
    • Listener 2: The dog starts barking (Admin gets an SMS).
    • Listener 3: The security camera starts recording (Log entry created).


How We Architect the Project based on Actions, Events, Listeners and Services.

The Workflow: Action → Event → Listener

Here is how the “Doorbell” looks in code structure:

1. The Action: PlaceOrderAction

This is where the core work happens. It creates the record in the database.

  • Purpose: To do the “Main Job.”
  • Responsibility: Validate data, save to the orders table, and return the Order object.
  • The Trigger: Inside this action, at the very end, you “press the doorbell” by dispatching the event: OrderPlaced::dispatch($order);.

2. The Event: OrderPlaced

This is just the “Data Carrier.” It doesn’t do anything; it just holds the Order information so the listeners can use it.

3. The Listeners: SendEmail, NotifyAdmin, SyncToXE

These are the reactions.

  • Purpose: To handle the secondary tasks that don’t need to happen “instantly” for the user to see a success message.

The “Rules of Thumb” for Team

ScenarioUse an Action if…Use a Listener if…
User Waiting?Yes. The user needs the result now to see the next page.No. It can happen in the background (1–5 seconds later).
Core Task?Yes. If this fails, the whole process should stop.No. If the “Email” fails, we still want the order to be saved.
Return Value?Yes. You need the Action to return a Price, an ID, or a Model.No. Listeners don’t return anything to the Controller.
ExamplesCalculateQuote, CreateUser, ApplyDiscount.SendWelcomeEmail, LogActivity, ClearCache.

Where the “Service” fits in Code

A Service should be used whenever you are dealing with:

  1. 3rd Party APIs (XE.com, Stripe, AWS, Twilio).
  2. Complex, Reusable Logic that isn’t tied to one specific “Task” (e.g., a PdfGeneratorService).

Example: The XE.com Currency Logic

Let’s see how the Service makes your Action cleaner.

1. The Service (app/Services/Currency/XeService.php)

This file only cares about how to talk to XE.com. It doesn’t know about your “Orders” or “Quotes.”

PHP

class XeService {
    public function getLatestRate(string $base, string $target): float {
        // All the messy Guzzle/CURL code goes here
        // Handle API keys, timeouts, and headers
        return 83.50; 
    }
}

2. The Action (app/Actions/Currency/UpdateLocalRate.php)

The Action calls the specialist (Service) to get the data, then does the “Business Task” (saving it).

PHP

class UpdateLocalRate {
    public function __construct(protected XeService $xe) {}

    public function execute(string $currencyCode) {
        $rate = $this->xe->getLatestRate('INR', $currencyCode);
        
        return Currency::updateOrCreate(
            ['code' => $currencyCode],
            ['rate' => $rate]
        );
    }
}

The Full Architecture

LayerResponsibilityAnalogy
ControllerReceives the request and calls the Action.The Front Desk.
ActionThe “Manager.” Coordinates the business logic.The Project Manager.
ServiceThe “Specialist.” Handles technical/external tasks.The Specialized Developer.
Event/ListenerThe “After-effects.” Sends emails, logs data.The Support Team.

The Branching Strategy

  • main (The Holy Branch): This branch is production-ready. Always. No one ever pushes code directly here. It is only updated via Pull Requests (PRs).
  • develop (The Integration Branch): This is where features are merged before going to production. It’s the “staging” version of your site.
  • feature/ (Work Branches): Every task gets its own branch.
    • Example: feature/xe-currency-integration or feature/hotel-booking-action.

Git Rules for Our Team

To keep the repository clean as project scale, set these three non-negotiable rules:

A. Atomic Commits

RULE: “One commit per thought.”

  • Bad: One giant commit called “Finished Currency Feature.” (Hard to review).
  • Good:
    • feat: added XeService for API calls
    • feat: created UpdateCurrencyRate action
    • feat: added scheduled job for 6-hour sync

B. The “Pull Before Push” Rule

Before starting any work, developers must run git pull origin develop. This ensures they are working on the latest version and reduces merge conflicts in those 1,000-line HelperControllers (which we are now replacing with Actions anyway!).

C. Descriptive Branch Names

Use a naming convention so everyone know what is happening just by looking at the branch list:

  • feat/ for new features.
  • fix/ for bug fixes.
  • refactor/ for cleaning up old code (like moving logic from a Helper to an Action).

In a professional Laravel setup, we name things based on their role (Action, Service, Job) rather than just their “subject.”


1. Action Naming (The “Verb-First” Rule)

Since an Action does one specific task, the name should always start with a Verb.

  • Pattern: Verb + Subject + Action
  • Good: CreateOrderAction, ApplyCouponAction, CalculateHotelStayAction.
  • Bad: OrderManager, CouponHelper, HotelLogic.

Why? When you look at the file list, you instantly see a list of capabilities (e.g., “This app can Create Order, Apply Coupon, etc.”).


2. Service Naming (The “Specialist” Rule)

Services are usually wrappers for 3rd party tools or complex technical domains.

  • Pattern: ProviderName + Domain + Service
  • Good: XeCurrencyService, StripePaymentService, TwilioSmsService.
  • Bad: CurrencyAPI, PaymentProcessor.

Why? If you switch from XE to Google Finance, you create GoogleCurrencyService. The name tells you exactly which external provider is being touched.


3. Job Naming (The “Outcome” Rule)

Jobs describe a background task that will eventually be finished.

  • Pattern: Task + Job (or a descriptive phrase)
  • Good: SyncExchangeRatesJob, ProcessMonthlyInvoicesJob, SendBookingReminderJob.
  • Bad: CurrencyTask, InvoiceRunner.

4. Event & Listener Naming (The “Past & Present” Rule)

This is the most important for maintaining your “1 million entries” scale.

  • Events (Past Tense): Something that already happened.
    • OrderPlaced, RoomBooked, QuoteRequested.
  • Listeners (Action-Oriented): What we are doing in response.
    • SendOrderConfirmationEmail, UpdateInventoryStock, NotifyManagerOfNewQuote.

5. Folder & File Organization

To make your code reviews fast, keep your directory structure as flat and descriptive as possible:

Plaintext

app/
├── Actions/
│   ├── Orders/
│   │   └── CreateOrderAction.php
│   └── Quotes/
│       └── CalculateCustomQuoteAction.php
├── Services/
│   ├── Currency/
│   │   └── XeCurrencyService.php
│   └── Payments/
│       └── RazorpayService.php

6. Variables & Database Columns (The “Clarity” Rule)

For projects especially with high-volume data, avoid abbreviations.

  • Boolean Columns: Use is_, has_, or was_.
    • is_paid, has_discount, was_refunded.
  • Date Columns: Always end with _at.
    • booked_at, cancelled_at, rates_updated_at.
  • Foreign Keys: Always singular_model_id.
    • user_id, hotel_room_id.
Scroll to Top