What you get in the generated ZIP
salesforce/
├── sfdx-project.json # packageDirectories, sourceApiVersion
├── config/
│ └── project-scratch-def.json # Scratch org: edition, features, settings
├── force-app/main/default/
│ ├── objects/
│ │ └── {Entity}__c/
│ │ └── {Entity}__c.object-meta.xml # Custom object: fields, FLS, relationships,
│ │ # listViews, searchLayouts
│ ├── classes/
│ │ ├── {Entity}Service.cls # Service: SOQL (WITH SECURITY_ENFORCED),
│ │ │ # bulkified DML, governor-limit-safe
│ │ ├── {Entity}TriggerHandler.cls # Handler: beforeInsert, beforeUpdate,
│ │ │ # afterInsert, afterUpdate, beforeDelete
│ │ ├── {Entity}Test.cls # Test: @TestSetup, positive + negative +
│ │ │ # bulk (200+ records), ≥75% coverage
│ │ ├── AuditTrailService.cls # Present when audit_trail capability selected
│ │ └── {Entity}QueueableJob.cls # Present when async_processing declared
│ ├── triggers/
│ │ └── {Entity}Trigger.trigger # One trigger per object — delegates to handler
│ ├── lwc/
│ │ └── {entity}List/
│ │ ├── {entity}List.html # Wire adapter template + conditional rendering
│ │ ├── {entity}List.js # @wire, @track, @api, imperative Apex
│ │ ├── {entity}List.css
│ │ └── {entity}List.js-meta.xml # targets: App page + Record page
│ ├── permissionsets/
│ │ └── {AppName}_Admin.permissionset-meta.xml
│ └── namedCredentials/
│ └── {Integration}.namedCredential-meta.xml
└── .github/workflows/
└── salesforce-ci.yml # validate → test → deploy sandbox/prod
What's already wired
Trigger-handler pattern — correctly implemented:
The trigger contains only context routing (if (Trigger.isBefore)...). All business logic lives in the handler. Zero inline DML or SOQL in the trigger file. This is the code review gate most teams fail on a hand-rolled first attempt.
Bulkification — every SOQL and DML is list-based:
Every query has a LIMIT. Every DML operates on List<SObject>, not a single record. Governor limits don't bite you in bulk operations (data loader, batch jobs, integrations).
WITH SECURITY_ENFORCED on every SOQL:
Field-level security and object-level security enforced at the query level. Not an afterthought — baked into every generated service method.
Test class structure:
@TestSetupcreates reusable test data once- Positive tests assert the happy path
- Negative tests assert exceptions on invalid transitions
- Bulk tests create 200+ records and assert DML is within governor limits
- All required by Salesforce before production deployment — generated up front
State machine transitions (when declared in genome):
If your genome includes state transitions (e.g. Draft → Pending Review → Active → Closed), the handler emits a transition() method with from-state guards. Invalid transitions throw an AuraHandledException so the LWC can surface the error message to the user.
SFDX CI/CD pipeline:
- Uses Salesforce CLI v2 (
sfcommands, not deprecatedsfdx) - Validates against sandbox on every PR
- Deploys to production on merge to
main - JWT auth (no interactive login in CI)
What ships in docs/
docs/decisions/ADR-apex-trigger-handler-pattern.md— why one trigger per object, handler class, Unit of Work; rejects alternativesdocs/compliance/salesforce-security-posture.md— FLS, CRUD,WITH SECURITY_ENFORCED, permission set modeldocs/runbooks/salesforce-scratch-to-prod.md— scratch org setup, push metadata, run tests, promote to sandbox, deploy to production
Internal links
- Salesforce Apex integration for the full generated file reference
- Dynamics 365 customisation use case for the Microsoft CRM/ERP equivalent
CTA
Try it — free plan, no credit card. archiet.com.
Generate a Salesforce project from your entity model. Open the trigger and the handler. Check whether it's the shape your org's code review checklist would pass.