Skip to main content

Execution Flows

Complete execution traces showing service contracts, plugins, events, observers, and database operations for key customer operations.

Step-by-Step 8 Flow Traces

Customer Registration Flow

Entry Point

Controller: Magento\Customer\Controller\Account\CreatePost::execute()

Route: POST /customer/account/createPost

Area: frontend

Execution Sequence

1

HTTP POST Request

User submits registration form with customer details (firstname, lastname, email, password, addresses)

2

Controller Pre-checks

  • Check if already logged in (exit if true)
  • Verify registration is allowed in configuration
  • Validate form_key (CSRF protection)
  • Regenerate session ID for security
3

Extract Customer Data

Convert POST data to CustomerInterface DTO

  • CustomerExtractor::extract('customer_account_create')
  • Build CustomerInterface DTO from POST data
  • Extract address if 'create_address' checkbox checked
  • Set newsletter subscription flag as extension attribute
4

Create Account

Call service contract: AccountManagementInterface::createAccount($customer, $password, $redirectUrl)

5

AccountManagement Internal Logic

  • Validate password strength against security policy
  • Check if email already exists (isEmailAvailable)
  • Hash password using Argon2ID13 algorithm
  • Set customer group (default or auto-assigned)
  • Set website_id from current store
  • Generate confirmation token if required
6

Repository Save

Persist customer to database via CustomerRepositoryInterface::save($customer, $passwordHash)

7

Transaction Wrapper Plugin (BEFORE)

  • Plugin: TransactionWrapper::beforeSave() [sortOrder: -1]
  • Opens database transaction
  • Ensures atomicity of customer + address save
8

Database Persistence

  • Validate customer data (email format, required fields)
  • Check unique email constraint (email + website_id)
  • Convert DTO to Model (Customer\Model\Customer)
  • INSERT into customer_entity (main table)
  • INSERT into customer_entity_* (EAV attribute values)
  • INSERT into customer_address_entity (if address provided)
9

Event Dispatch: customer_save_after_data_object

Event data: customerDataObject, origCustomerDataObject

10

Transaction Commit

  • Plugin: TransactionWrapper::afterSave()
  • Commits database transaction
  • Rolls back on any exception
11

Post-Save Actions

  • Process newsletter subscription (if opted in)
  • Send welcome email (or confirmation email if required)
  • Return saved CustomerInterface DTO
12

Registration Success Event

Dispatch event: customer_register_success

  • Check confirmation status
  • If confirmed: Log customer in automatically
  • If pending: Show "check email" message
13

Session Creation (if auto-confirmed)

  • Session::setCustomerDataAsLoggedIn($customer)
  • Dispatch event: customer_login
  • Dispatch event: customer_data_object_login
  • Delete mage-cache-sessid cookie (FPC invalidation)
14

HTTP Response

Redirect to customer dashboard or requested URL

Key Points

Transaction Safety

The TransactionWrapper plugin (sortOrder -1) runs BEFORE all other plugins and ensures customer + addresses are saved atomically. Rolls back on any validation or database error.

Email Confirmation

If customer/create_account/confirm is enabled, confirmation is required. Customer receives email with confirmation link. Account is inactive until confirmed.

Newsletter Subscription

Stored as extension attribute during registration. Processed by AccountManagement after customer save. Separate subscription record created in newsletter tables.

Customer Login Flow

Entry Point

Controller: Magento\Customer\Controller\Account\LoginPost::execute()

Route: POST /customer/account/loginPost

Area: frontend

Execution Sequence

1

HTTP POST Request

User submits login form credentials (login[username], login[password])

2

Controller Pre-checks

  • Validate form_key (CSRF protection)
  • Check if already logged in (exit if true)
  • Validate POST method
3

Authenticate Customer

Call service contract: AccountManagementInterface::authenticate($email, $password)

4

Authentication Logic

  • Load customer by email + website_id
  • Check if customer account is active
  • Check if email confirmation is pending
  • Delegate to AuthenticationInterface for password verification
5

Password Validation

  • Load customer model by ID
  • Retrieve password_hash from customer_entity
  • Verify password using Encryptor::validateHash()
  • Check if password hash algorithm is outdated
  • Process lock mechanism (failed login attempts)
  • Throw exception if authentication fails
6

Authentication Event

Dispatch event: customer_customer_authenticated

Observer: CustomerGroupAuthenticate validates customer group is active

7

Password Hash Upgrade (if needed)

  • If hash uses old algorithm (SHA256, MD5)
  • Rehash with current algorithm (Argon2ID13)
  • UPDATE customer_entity.password_hash
  • Handled by UpgradeCustomerPasswordObserver
8

Create Customer Session

  • Session::setCustomerDataAsLoggedIn($customer)
  • Store customer data in session storage
  • Set customer_id, customer_group_id in session
  • Regenerate session ID (prevent session fixation)
  • Set session cookie
9

Login Events

Dispatch events: customer_login, customer_data_object_login

  • Observer: LogLastLoginAtObserver - UPDATE customer_log SET last_login_at = NOW()
  • Observer: Visitor\BindCustomerLoginObserver - UPDATE customer_visitor SET customer_id = ?
10

Post-Login Actions

  • Set success message
  • Clear cart persistent data if needed
  • Determine redirect URL (dashboard or referer)
  • Delete mage-cache-sessid cookie (FPC invalidation)
11

HTTP Response

Redirect to dashboard or requested page

Key Points

Session Security

Session ID regenerated on login to prevent session fixation attacks. Session validated on each request. Customer group stored in session for performance.

Failed Login Attempts

Tracked in customer_entity.failures_num, first_failure, lock_expires. After X failures (configurable), account temporarily locked. Lock duration configurable in admin.

Database Updates

customer_log.last_login_at timestamp updated. customer_visitor.customer_id links visitor session to customer. customer_entity.password_hash upgraded if using old algorithm.

Customer Save Flow

Entry Point

Service Contract: CustomerRepositoryInterface::save(CustomerInterface $customer, $passwordHash = null)

Implementation: Model\ResourceModel\CustomerRepository

Note: Core flow for any customer update operation

Execution Sequence

1

Repository Save Called

Origin: Controller, API, Command, Observer, etc.

2

Transaction Wrapper (BEFORE)

  • Plugin: TransactionWrapper::beforeSave() [sortOrder: -1]
  • Begin database transaction
  • Store original customer data for rollback if needed
3

Load Original Customer

  • If $customer->getId() exists: Load existing customer
  • Store original for comparison in events
  • If new customer: Original is null
4

Validation

  • Validate email format
  • Validate required fields (firstname, lastname, etc.)
  • Check email uniqueness (email + website_id)
  • Validate custom attribute values
  • Throw InputException if validation fails
5

DTO to Model Conversion

  • Convert CustomerInterface DTO to Customer Model
  • Set password_hash if provided
  • Set custom EAV attributes
  • Prepare for database persistence
6

Database Persistence

  • ResourceModel\Customer::save($customerModel)
  • UPDATE customer_entity (if existing) OR INSERT (if new)
  • UPDATE/INSERT customer_entity_varchar (EAV attributes)
  • UPDATE/INSERT customer_entity_int, _datetime, etc.
  • Update updated_at timestamp
7

Convert Back to DTO

  • Load fresh data from database (to get auto-IDs, etc.)
  • Convert Customer Model to CustomerInterface DTO
  • Prepare for return and event dispatch
8

Critical Event: customer_save_after_data_object

Event data: customerDataObject (new state), origCustomerDataObject (old state)

  • Observer: UpgradeOrderCustomerEmailObserver - If email changed: UPDATE sales_order SET customer_email = new email
  • Observer: UpgradeQuoteCustomerEmailObserver - If email changed: UPDATE quote SET customer_email = new email
9

Transaction Commit

  • Plugin: TransactionWrapper::afterSave()
  • Commit database transaction
  • All changes persisted atomically
  • On exception: Rollback transaction and re-throw
10

Return Updated DTO

Returns CustomerInterface DTO to caller with updated data including auto-generated IDs

Key Points

Transaction Wrapper Pattern

Critical plugin with sortOrder -1 runs first. Wraps entire save operation in database transaction. Ensures customer + addresses + EAV attributes saved atomically. Rolls back on ANY exception.

Email Synchronization Side Effect

When customer email changes, observers update related records. All historical orders get new email for customer lookup. Active quote gets new email for cart recovery emails. This is a side effect of customer save, not explicit business logic.

Database Tables Modified

customer_entity (main record), customer_entity_varchar/int/etc (EAV attributes), sales_order (email sync via observer), quote (email sync via observer)

Customer Email Change Flow

Entry Point

Trigger: Any operation that calls CustomerRepositoryInterface::save() with a changed email

Note: Critical business flow - email is primary customer identifier

Execution Sequence

1

Customer Email Updated

User updates email in profile or admin changes it via CustomerRepository::save()

2

Repository Save Process

See "Customer Save Flow" above for full details. Database updated with new email.

3

Email Change Event

Event: customer_save_after_data_object

customerDataObject.email = "new@example.com"

origCustomerDataObject.email = "old@example.com"

4

Order Email Synchronization

Observer: UpgradeOrderCustomerEmailObserver

  • Check if origCustomerDataObject exists (not new customer)
  • Compare old email vs. new email
  • If changed: Build SearchCriteria (customer_id + old email)
  • OrderRepository::getList($searchCriteria)
  • Iterate all matching orders
  • Set new email: $order->setCustomerEmail($newEmail)
  • Collection save: $orders->save()
  • Side Effect: Historical orders now searchable by new email
5

Quote Email Synchronization

Observer: UpgradeQuoteCustomerEmailObserver

  • Check if origCustomerDataObject exists (not new customer)
  • Compare old email vs. new email
  • If changed: Try to load active quote
  • QuoteRepository::getForCustomer($customerId)
  • If quote exists: $quote->setCustomerEmail($newEmail)
  • QuoteRepository::save($quote)
  • Catch NoSuchEntityException (no active cart)
  • Side Effect: Cart recovery emails sent to new address
6

Database State After Email Change

  • customer_entity.email = "new@example.com"
  • sales_order.customer_email = "new@example.com" (all orders)
  • quote.customer_email = "new@example.com" (active cart)

Key Points

Why Synchronize Email?

Order Lookup: Admin "View Orders" searches by customer_email. Customer Reports: Order reports grouped by customer_email. Cart Recovery: Abandoned cart emails sent to quote.customer_email. Data Integrity: Email is denormalized across tables for performance.

Performance Implications

Order Sync may update many rows (customer with 100s of orders). Uses collection save for efficient bulk UPDATE. Runs in same transaction as customer save (atomic).

Email Uniqueness Constraint

Email must be unique within website scope. Validated BEFORE observers run. Constraint: UNIQUE KEY (email, website_id)

Address Save Flow

Entry Point

Service Contract: AddressRepositoryInterface::save(AddressInterface $address)

Implementation: Model\ResourceModel\AddressRepository

Execution Sequence

1

Address Save Called

Origin: Frontend form, admin, API, checkout

2

Validation

  • Validate address has parent customer_id
  • Validate region matches country
  • Validate postal code format (if country requires)
  • Validate required fields (street, city, etc.)
  • Throw InputException if validation fails
3

Before Save Event

Event: customer_address_save_before

Observer: BeforeAddressSaveObserver validates VAT number if provided. Checks EU VAT validation service (if enabled). Sets vat_is_valid flag.

4

Database Persistence

  • ResourceModel\Address::save($addressModel)
  • UPDATE customer_address_entity (if existing) OR INSERT (if new)
  • UPDATE/INSERT customer_address_entity_varchar (EAV)
  • UPDATE/INSERT customer_address_entity_int, _text, etc.
  • Update updated_at timestamp
5

Update Default Address Flags

  • If address marked as default_billing:
  • UPDATE customer_entity SET default_billing = $addressId
  • If address marked as default_shipping:
  • UPDATE customer_entity SET default_shipping = $addressId
  • Ensures customer record points to default addresses
6

After Save Event - VAT Processing

Event: customer_address_save_after

Observer: AfterAddressSaveObserver - Complex VAT handling logic. If VAT validation result changed, may trigger customer group change. Updates customer_entity.group_id. Invalidates customer session/cache.

Important

Can modify customer during address save!

Key Points

VAT Validation Side Effects

BeforeAddressSaveObserver validates VAT number with EU service. AfterAddressSaveObserver may change customer group based on VAT status. CRITICAL: Address save can trigger customer save (group change). This is B2B-specific functionality.

Default Address Handling

Customer can have one default billing and one default shipping address. Flags stored in customer_address_entity.is_default_billing/shipping. Customer entity also stores FKs: default_billing, default_shipping for fast lookup.

Password Reset Flow

Entry Point

Controller: Magento\Customer\Controller\Account\ForgotPasswordPost::execute()

Route: POST /customer/account/forgotPasswordPost

Execution Sequence

1

User Requests Password Reset

Form data: email

2

Generate Reset Token

  • AccountManagementInterface::initiatePasswordReset()
  • Load customer by email + website_id
  • Generate random reset token (UUID)
  • Set expiration timestamp (default: 1 hour)
  • Update customer: rp_token = $token, rp_token_created_at = NOW()
3

Send Password Reset Email

Email contains link: /customer/account/createPassword?token=...&id=...

4

User Submits New Password

  • AccountManagementInterface::resetPassword($email, $token, $newPassword)
  • Validate token matches customer.rp_token
  • Validate token not expired (created_at + expiry)
  • Hash new password (Argon2ID13)
  • Update: password_hash = $newHash, rp_token = NULL

Key Points

Token Security

Token is random UUID (not predictable). Token stored in customer_entity.rp_token with expiration time (default 1 hour). Token invalidated (set to NULL) after successful use. One-time use only.

Security Considerations

Rate limiting on password reset requests (prevent email bombing). Token expiration prevents old emails being used. No indication if email exists (security - prevent enumeration).

Event and Observer Reference

Events Dispatched by Customer Module

Event When Area
customer_register_success After successful registration frontend
customer_save_after_data_object After customer DTO save global
customer_login After successful login frontend
customer_data_object_login After successful login (DTO) frontend
customer_logout After logout frontend
customer_customer_authenticated During authentication frontend
customer_address_save_before Before address save global
customer_address_save_after After address save global

Critical Observers in Customer Module

Observer Event Side Effects
UpgradeOrderCustomerEmailObserver customer_save_after_data_object UPDATE sales_order
UpgradeQuoteCustomerEmailObserver customer_save_after_data_object UPDATE quote
BeforeAddressSaveObserver customer_address_save_before May set VAT flags
AfterAddressSaveObserver customer_address_save_after May change customer group!
LogLastLoginAtObserver customer_login UPDATE customer_log
Visitor\BindCustomerLoginObserver customer_data_object_login UPDATE customer_visitor
UpgradeCustomerPasswordObserver customer_customer_authenticated UPDATE customer_entity