@salesforce/afv-skills 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/package.json +3 -3
  2. package/skills/creating-webapp/SKILL.md +0 -2
  3. package/skills/generating-apex/SKILL.md +253 -0
  4. package/skills/generating-apex/assets/abstract.cls +128 -0
  5. package/skills/generating-apex/assets/batch.cls +125 -0
  6. package/skills/generating-apex/assets/domain.cls +102 -0
  7. package/skills/generating-apex/assets/dto.cls +108 -0
  8. package/skills/generating-apex/assets/exception.cls +51 -0
  9. package/skills/generating-apex/assets/interface.cls +25 -0
  10. package/skills/generating-apex/assets/queueable.cls +92 -0
  11. package/skills/generating-apex/assets/schedulable.cls +75 -0
  12. package/skills/generating-apex/assets/selector.cls +92 -0
  13. package/skills/generating-apex/assets/service.cls +69 -0
  14. package/skills/generating-apex/assets/utility.cls +97 -0
  15. package/skills/generating-apex/references/AccountDeduplicationBatch.cls +148 -0
  16. package/skills/generating-apex/references/AccountSelector.cls +193 -0
  17. package/skills/generating-apex/references/AccountService.cls +201 -0
  18. package/skills/generating-apex-test/SKILL.md +108 -0
  19. package/skills/generating-apex-test/assets/test-class-template.cls +124 -0
  20. package/skills/generating-apex-test/assets/test-data-factory-template.cls +112 -0
  21. package/skills/generating-apex-test/references/assertion-patterns.md +165 -0
  22. package/skills/generating-apex-test/references/async-testing.md +276 -0
  23. package/skills/generating-apex-test/references/mocking-patterns.md +219 -0
  24. package/skills/generating-apex-test/references/test-data-factory.md +176 -0
  25. package/skills/generating-experience-react-site/SKILL.md +11 -0
  26. package/skills/generating-flexipage/SKILL.md +39 -57
  27. package/skills/searching-media/SKILL.md +342 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/afv-skills",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "Salesforce skills for Agentforce Vibes",
5
5
  "license": "CC-BY-NC-4.0",
6
6
  "files": [
@@ -11,8 +11,8 @@
11
11
  "registry": "https://registry.npmjs.org"
12
12
  },
13
13
  "devDependencies": {
14
- "@salesforce/webapp-template-app-react-sample-b2e-experimental": "^1.112.7",
15
- "@salesforce/webapp-template-app-react-sample-b2x-experimental": "^1.112.7",
14
+ "@salesforce/webapp-template-app-react-sample-b2e-experimental": "^1.116.6",
15
+ "@salesforce/webapp-template-app-react-sample-b2x-experimental": "^1.116.6",
16
16
  "@types/js-yaml": "^4.0.9",
17
17
  "js-yaml": "^4.1.1",
18
18
  "tsx": "^4.21.0"
@@ -1,8 +1,6 @@
1
1
  ---
2
2
  name: creating-webapp
3
3
  description: "Use this skill when creating or setting up a new SFDX React web application. Covers first steps, npm install, skills-first protocol, deployment order, and core web app rules."
4
- paths:
5
- - "**/webapplications/**/*"
6
4
  ---
7
5
 
8
6
  # First Steps (MUST FOLLOW)
@@ -0,0 +1,253 @@
1
+ ---
2
+ name: generating-apex
3
+ description: Generate production-ready Apex classes for Salesforce following enterprise best practices. Covers service, selector, domain, batch, queueable, schedulable, DTO, utility, interface, abstract, and custom exception classes. Use this skill when the user asks to create, build, generate, scaffold, or refactor an Apex class. SCOPE Apex classes only — does not apply to triggers or unit tests.
4
+ ---
5
+
6
+ # Apex Class
7
+
8
+ Generate well-structured, production-ready Apex classes for Salesforce development.
9
+
10
+ ## Scope
11
+
12
+ **Generates:**
13
+ - Service classes (business logic layer)
14
+ - Selector / query classes (SOQL encapsulation)
15
+ - Domain classes (SObject-specific logic)
16
+ - Batch Apex classes (`Database.Batchable`)
17
+ - Queueable Apex classes (`Queueable`, optionally `Finalizer`)
18
+ - Schedulable Apex classes (`Schedulable`)
19
+ - DTO / wrapper / request-response classes
20
+ - Utility / helper classes
21
+ - Custom exception classes
22
+ - Interfaces and abstract classes
23
+
24
+ **Does NOT generate:**
25
+ - Triggers (use a trigger framework skill)
26
+ - Unit tests (use a test writer skill)
27
+ - Aura controllers
28
+ - LWC JavaScript controllers
29
+
30
+ ---
31
+
32
+ ## Gathering Requirements
33
+
34
+ Before generating code, gather these inputs from the user (ask if not provided):
35
+
36
+ 1. **Class type** — Service, Selector, Batch, Queueable, Schedulable, Domain, DTO, Utility, Interface, Abstract, Exception
37
+ 2. **Class name** — or enough context to derive a meaningful name
38
+ 3. **SObject(s) involved** — if applicable
39
+ 4. **Business requirements** — plain-language description of what the class should do
40
+ 5. **Optional preferences:**
41
+ - Sharing model (`with sharing`, `without sharing`, `inherited sharing`)
42
+ - Access modifier (`public` or `global`)
43
+ - API version (default: `62.0`)
44
+ - Whether to include ApexDoc comments (default: yes)
45
+
46
+ If the user provides a clear, complete request, generate immediately without unnecessary back-and-forth.
47
+
48
+ ---
49
+
50
+ ## Code Standards — ALWAYS Follow These
51
+
52
+ ### Separation of Concerns
53
+ - **Service classes** contain business logic. They call Selectors for data and may call Domain classes for SObject-specific behavior.
54
+ - **Selector classes** encapsulate all SOQL queries. Services never contain inline SOQL.
55
+ - **Domain classes** encapsulate SObject-specific logic (field defaults, validation, transformation).
56
+ - Keep classes focused on a single responsibility.
57
+
58
+ ### Bulkification
59
+ - All methods must operate on collections (`List`, `Set`, `Map`) by default.
60
+ - Never accept a single SObject when a `List<SObject>` is appropriate.
61
+ - Provide convenience overloads for single-record callers only when it makes the API cleaner, and have them delegate to the bulk method.
62
+
63
+ ### Governor Limit Safety
64
+ - **No SOQL or DML inside loops.** Ever.
65
+ - Collect IDs/records first, query/DML once outside the loop.
66
+ - Use `Limits` class checks in batch/bulk operations where appropriate.
67
+ - Prefer `Database.insert(records, false)` with error handling in batch contexts.
68
+
69
+ ### Sharing Model
70
+ - Default to `with sharing` unless the user specifies otherwise.
71
+ - If `without sharing` or `inherited sharing` is used, add an ApexDoc `@description` comment explaining WHY.
72
+
73
+ ### Naming Conventions
74
+
75
+ | Class Type | Pattern | Example |
76
+ |-------------|-------------------------------|-------------------------------|
77
+ | Service | `{SObject}Service` | `AccountService` |
78
+ | Selector | `{SObject}Selector` | `AccountSelector` |
79
+ | Domain | `{SObject}Domain` | `OpportunityDomain` |
80
+ | Batch | `{Descriptive}Batch` | `AccountDeduplicationBatch` |
81
+ | Queueable | `{Descriptive}Queueable` | `ExternalSyncQueueable` |
82
+ | Schedulable | `{Descriptive}Schedulable` | `DailyCleanupSchedulable` |
83
+ | DTO | `{Descriptive}DTO` | `AccountMergeRequestDTO` |
84
+ | Wrapper | `{Descriptive}Wrapper` | `OpportunityLineWrapper` |
85
+ | Utility | `{Descriptive}Util` | `StringUtil`, `DateUtil` |
86
+ | Interface | `I{Descriptive}` | `INotificationService` |
87
+ | Abstract | `Abstract{Descriptive}` | `AbstractIntegrationService` |
88
+ | Exception | `{Descriptive}Exception` | `AccountServiceException` |
89
+
90
+ ### ApexDoc Comments
91
+ Include ApexDoc on every `public` and `global` method and on the class itself:
92
+
93
+ ```apex
94
+ /**
95
+ * @description Brief description of what the class does
96
+ * @author Generated by Apex Class Writer Skill
97
+ */
98
+ ```
99
+
100
+ ```apex
101
+ /**
102
+ * @description Brief description of what the method does
103
+ * @param paramName Description of the parameter
104
+ * @return Description of the return value
105
+ * @example
106
+ * List<Account> results = AccountService.deduplicateAccounts(accountIds);
107
+ */
108
+ ```
109
+
110
+ ### Null Safety
111
+ - Use guard clauses at the top of methods for null/empty inputs.
112
+ - Use safe navigation (`?.`) where appropriate.
113
+ - Return empty collections rather than `null`.
114
+
115
+ ### Constants
116
+ - No magic strings or numbers in logic.
117
+ - Use `private static final` constants or a dedicated constants class.
118
+ - Use `Label.` custom labels for user-facing strings when appropriate.
119
+
120
+ ### Custom Exceptions
121
+ - Each service class should define or reference a corresponding custom exception.
122
+ - Inner exception classes are preferred for simple cases: `public class AccountServiceException extends Exception {}`
123
+ - Include meaningful error messages with context.
124
+
125
+ ### Error Handling
126
+ - Catch specific exceptions, not generic `Exception` unless re-throwing.
127
+ - Log errors meaningfully (assume a logging utility exists or stub one).
128
+ - In batch contexts, use `Database.SaveResult` / `Database.UpsertResult` with partial success.
129
+
130
+ ---
131
+
132
+ ## Output Format
133
+
134
+ For every class, produce TWO files:
135
+
136
+ 1. **`{ClassName}.cls`** — The Apex class source code
137
+ 2. **`{ClassName}.cls-meta.xml`** — The metadata file
138
+
139
+ ### Meta XML Template
140
+
141
+ ```xml
142
+ <?xml version="1.0" encoding="UTF-8"?>
143
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
144
+ <apiVersion>{API_VERSION}</apiVersion>
145
+ <status>Active</status>
146
+ </ApexClass>
147
+ ```
148
+
149
+ Default `apiVersion` is `62.0` unless the user specifies otherwise.
150
+
151
+ ---
152
+
153
+ ## Class Type–Specific Instructions
154
+
155
+ ### Service Classes
156
+ - Read and follow: `assets/service.cls`
157
+ - Stateless — no instance variables holding mutable state
158
+ - All public methods should be `static` unless there's a compelling reason for instance methods
159
+ - Delegate queries to a Selector class
160
+ - Wrap business logic errors in a custom exception
161
+
162
+ ### Selector Classes
163
+ - Read and follow: `assets/selector.cls`
164
+ - One Selector per SObject (or per logical query domain)
165
+ - Return `List<SObject>` or `Map<Id, SObject>`
166
+ - Accept filter criteria as method parameters, not hardcoded
167
+ - Include a private method that returns the base field list to keep DRY
168
+
169
+ ### Domain Classes
170
+ - Read and follow: `assets/domain.cls`
171
+ - Encapsulate field-level defaults, derivations, and validations
172
+ - Operate on `List<SObject>` — designed to be called from triggers or services
173
+ - No SOQL or DML — only in-memory SObject manipulation
174
+
175
+ ### Batch Classes
176
+ - Read and follow: `assets/batch.cls`
177
+ - Implement `Database.Batchable<SObject>` and optionally `Database.Stateful`
178
+ - Use `Database.QueryLocator` in `start()` for large datasets
179
+ - Handle partial failures in `execute()` using `Database.SaveResult`
180
+ - Implement meaningful `finish()` — at minimum, log completion
181
+
182
+ ### Queueable Classes
183
+ - Read and follow: `assets/queueable.cls`
184
+ - Implement `Queueable` and optionally `Database.AllowsCallouts`
185
+ - Accept data through the constructor — queueables are stateful
186
+ - For chaining, include guard logic to prevent infinite chains
187
+ - Optionally implement `Finalizer` for error recovery
188
+
189
+ ### Schedulable Classes
190
+ - Read and follow: `assets/schedulable.cls`
191
+ - Implement `Schedulable`
192
+ - Keep `execute()` lightweight — delegate to a Batch or Queueable
193
+ - Include a static method that returns a CRON expression for convenience
194
+ - Document the expected schedule in ApexDoc
195
+
196
+ ### DTO / Wrapper Classes
197
+ - Read and follow: `assets/dto.cls`
198
+ - Use `public` properties — no getters/setters unless validation is needed
199
+ - Include a no-arg constructor and optionally a parameterized constructor
200
+ - Implement `Comparable` if sorting is needed
201
+ - Keep them serialization-friendly (no transient state unless intentional)
202
+
203
+ ### Utility Classes
204
+ - Read and follow: `assets/utility.cls`
205
+ - All methods `public static`
206
+ - Class should be `public with sharing` with a `private` constructor to prevent instantiation
207
+ - Group related utilities (e.g., `StringUtil`, `DateUtil`, `CollectionUtil`)
208
+ - Every method must be side-effect-free (no DML, no SOQL)
209
+
210
+ ### Interfaces
211
+ - Read and follow: `assets/interface.cls`
212
+ - Define the contract clearly with ApexDoc on every method signature
213
+ - Use meaningful names that describe the capability: `INotificationService`, `IRetryable`
214
+
215
+ ### Abstract Classes
216
+ - Read and follow: `assets/abstract.cls`
217
+ - Provide default implementations for common behavior
218
+ - Mark extension points as `protected virtual` or `protected abstract`
219
+ - Include a concrete example in the ApexDoc showing how to extend
220
+
221
+ ### Custom Exceptions
222
+ - Read and follow: `assets/exception.cls`
223
+ - Extend `Exception`
224
+ - Keep them simple — Apex exceptions don't support custom constructors well
225
+ - Name them descriptively: `AccountServiceException`, `IntegrationTimeoutException`
226
+
227
+ ---
228
+
229
+ ## Generation Workflow
230
+
231
+ 1. Determine the class type from the user's request
232
+ 2. Read the corresponding template from `assets/`
233
+ 3. Read relevant examples from `references/` if the class type has one
234
+ 4. Apply the user's requirements to the template pattern
235
+ 5. Generate the `.cls` file with full ApexDoc
236
+ 6. Generate the `.cls-meta.xml` file
237
+ 7. Present both files to the user
238
+ 8. Include a brief note on design decisions if any non-obvious choices were made
239
+
240
+ ---
241
+
242
+ ## Anti-Patterns to Avoid
243
+
244
+ - ❌ SOQL or DML inside loops
245
+ - ❌ Hardcoded IDs or record type names (use `Schema.SObjectType` or Custom Metadata)
246
+ - ❌ God classes that mix query + logic + DML
247
+ - ❌ `public` fields on service classes
248
+ - ❌ Returning `null` from methods that should return collections
249
+ - ❌ Generic `catch (Exception e)` without re-throwing or meaningful handling
250
+ - ❌ Business logic in Batch `start()` methods
251
+ - ❌ Tight coupling between classes — use interfaces for extensibility
252
+ - ❌ Magic strings or numbers
253
+ - ❌ Methods longer than ~40 lines — break them into private helpers
@@ -0,0 +1,128 @@
1
+ /**
2
+ * @description Abstract base class for {describe the family of classes this serves}.
3
+ * Provides common behavior and defines extension points for subclasses.
4
+ * Subclasses must implement the abstract methods to provide specific behavior.
5
+ * @author Generated by Apex Class Writer Skill
6
+ *
7
+ * @example
8
+ * // Extending this abstract class:
9
+ * public class SalesforceIntegrationService extends {ClassName} {
10
+ * protected override String getEndpoint() {
11
+ * return 'callout:Salesforce_API/services/data/v62.0';
12
+ * }
13
+ *
14
+ * protected override Map<String, String> getHeaders() {
15
+ * return new Map<String, String>{
16
+ * 'Content-Type' => 'application/json'
17
+ * };
18
+ * }
19
+ * }
20
+ */
21
+ public abstract with sharing class {ClassName} {
22
+
23
+ // ─── Constants ───────────────────────────────────────────────────────
24
+ private static final Integer DEFAULT_TIMEOUT_MS = 30000;
25
+
26
+ // ─── Protected State ─────────────────────────────────────────────────
27
+ protected Integer timeoutMs;
28
+
29
+ // ─── Constructor ─────────────────────────────────────────────────────
30
+
31
+ /**
32
+ * @description Initializes the base class with default configuration
33
+ */
34
+ protected {ClassName}() {
35
+ this.timeoutMs = DEFAULT_TIMEOUT_MS;
36
+ }
37
+
38
+ // ─── Abstract Methods (must be implemented by subclasses) ────────────
39
+
40
+ /**
41
+ * @description Returns the endpoint URL for this integration.
42
+ * Subclasses must provide their specific endpoint.
43
+ * @return The endpoint URL as a String
44
+ */
45
+ protected abstract String getEndpoint();
46
+
47
+ /**
48
+ * @description Returns the HTTP headers for this integration.
49
+ * Subclasses define their own required headers.
50
+ * @return Map of header name to header value
51
+ */
52
+ protected abstract Map<String, String> getHeaders();
53
+
54
+ // ─── Virtual Methods (can be overridden by subclasses) ───────────────
55
+
56
+ /**
57
+ * @description Hook called before the main operation executes.
58
+ * Override to add pre-processing logic.
59
+ * Default implementation does nothing.
60
+ * @param context Map of contextual data
61
+ */
62
+ protected virtual void beforeExecute(Map<String, Object> context) {
63
+ // Default: no-op — override in subclass if needed
64
+ }
65
+
66
+ /**
67
+ * @description Hook called after the main operation completes.
68
+ * Override to add post-processing logic.
69
+ * Default implementation does nothing.
70
+ * @param context Map of contextual data
71
+ * @param result The result from the operation
72
+ */
73
+ protected virtual void afterExecute(Map<String, Object> context, Object result) {
74
+ // Default: no-op — override in subclass if needed
75
+ }
76
+
77
+ // ─── Template Method (common workflow) ───────────────────────────────
78
+
79
+ /**
80
+ * @description Executes the operation using the template method pattern.
81
+ * Calls beforeExecute → doExecute → afterExecute in sequence.
82
+ * @param context Map of data needed for the operation
83
+ * @return The result of the operation
84
+ */
85
+ public Object execute(Map<String, Object> context) {
86
+ beforeExecute(context);
87
+
88
+ Object result;
89
+ try {
90
+ result = doExecute(context);
91
+ } catch (Exception e) {
92
+ handleError(e);
93
+ throw e;
94
+ }
95
+
96
+ afterExecute(context, result);
97
+ return result;
98
+ }
99
+
100
+ // ─── Protected Helpers ───────────────────────────────────────────────
101
+
102
+ /**
103
+ * @description Core execution logic — override this for the main operation.
104
+ * Default implementation throws — subclass must provide implementation.
105
+ * @param context Map of data needed for the operation
106
+ * @return The result of the operation
107
+ */
108
+ protected virtual Object doExecute(Map<String, Object> context) {
109
+ throw new UnsupportedOperationException(
110
+ 'Subclass must override doExecute()'
111
+ );
112
+ }
113
+
114
+ /**
115
+ * @description Error handler called when doExecute throws.
116
+ * Override to customize error handling (e.g., logging, retry).
117
+ * @param e The exception that was thrown
118
+ */
119
+ protected virtual void handleError(Exception e) {
120
+ System.debug(LoggingLevel.ERROR,
121
+ this.toString() + ' error: ' + e.getMessage() + '\n' + e.getStackTraceString()
122
+ );
123
+ }
124
+
125
+ // ─── Exception ───────────────────────────────────────────────────────
126
+
127
+ public class UnsupportedOperationException extends Exception {}
128
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @description Batch Apex class for {describe the batch operation}.
3
+ * Processes {SObject} records in configurable batch sizes.
4
+ * Implements Database.Stateful to track cumulative results across chunks.
5
+ * @author Generated by Apex Class Writer Skill
6
+ *
7
+ * @example
8
+ * // Execute with default batch size
9
+ * Database.executeBatch(new {ClassName}());
10
+ *
11
+ * // Execute with custom batch size
12
+ * Database.executeBatch(new {ClassName}(), 100);
13
+ */
14
+ public with sharing class {ClassName} implements Database.Batchable<SObject>, Database.Stateful {
15
+
16
+ // ─── Constants ───────────────────────────────────────────────────────
17
+ private static final Integer DEFAULT_BATCH_SIZE = 200;
18
+
19
+ // ─── Stateful Tracking ───────────────────────────────────────────────
20
+ private Integer totalProcessed = 0;
21
+ private Integer totalErrors = 0;
22
+ private List<String> errorMessages = new List<String>();
23
+
24
+ // ─── Constructor ─────────────────────────────────────────────────────
25
+
26
+ /**
27
+ * @description Default constructor
28
+ */
29
+ public {ClassName}() {
30
+ // Default configuration
31
+ }
32
+
33
+ // ─── Batchable Interface ─────────────────────────────────────────────
34
+
35
+ /**
36
+ * @description Defines the scope of records to process.
37
+ * Uses Database.QueryLocator for efficient large-dataset processing.
38
+ * @param bc The batch context
39
+ * @return QueryLocator for the records to process
40
+ */
41
+ public Database.QueryLocator start(Database.BatchableContext bc) {
42
+ return Database.getQueryLocator([
43
+ SELECT Id, Name
44
+ // TODO: Add fields needed for processing
45
+ FROM {SObject}
46
+ // TODO: Add WHERE clause to scope the records
47
+ ]);
48
+ }
49
+
50
+ /**
51
+ * @description Processes each batch of records.
52
+ * Uses Database.update with allOrNone=false for partial success handling.
53
+ * @param bc The batch context
54
+ * @param scope List of {SObject} records in the current batch
55
+ */
56
+ public void execute(Database.BatchableContext bc, List<{SObject}> scope) {
57
+ List<{SObject}> recordsToUpdate = new List<{SObject}>();
58
+
59
+ for ({SObject} record : scope) {
60
+ // TODO: Apply business logic to each record
61
+ recordsToUpdate.add(record);
62
+ }
63
+
64
+ if (!recordsToUpdate.isEmpty()) {
65
+ List<Database.SaveResult> results = Database.update(recordsToUpdate, false);
66
+ processResults(results);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * @description Performs post-processing after all batches complete.
72
+ * Logs a summary of the batch execution.
73
+ * @param bc The batch context
74
+ */
75
+ public void finish(Database.BatchableContext bc) {
76
+ String summary = String.format(
77
+ '{0} completed. Processed: {1}, Errors: {2}',
78
+ new List<Object>{
79
+ {ClassName}.class.getName(),
80
+ totalProcessed,
81
+ totalErrors
82
+ }
83
+ );
84
+
85
+ System.debug(LoggingLevel.INFO, summary);
86
+
87
+ if (!errorMessages.isEmpty()) {
88
+ System.debug(LoggingLevel.ERROR, 'Error details: ' + String.join(errorMessages, '\n'));
89
+ }
90
+
91
+ // TODO: Send completion notification email or post to chatter if needed
92
+ }
93
+
94
+ // ─── Private Helpers ─────────────────────────────────────────────────
95
+
96
+ /**
97
+ * @description Processes Database.SaveResult list, tracking successes and failures
98
+ * @param results List of SaveResult from a DML operation
99
+ */
100
+ private void processResults(List<Database.SaveResult> results) {
101
+ for (Database.SaveResult result : results) {
102
+ if (result.isSuccess()) {
103
+ totalProcessed++;
104
+ } else {
105
+ totalErrors++;
106
+ for (Database.Error err : result.getErrors()) {
107
+ errorMessages.add(
108
+ 'Record ' + result.getId() + ': ' +
109
+ err.getStatusCode() + ' - ' + err.getMessage()
110
+ );
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ // ─── Static Helpers ──────────────────────────────────────────────────
117
+
118
+ /**
119
+ * @description Convenience method to execute with default batch size
120
+ * @return The batch job Id
121
+ */
122
+ public static Id run() {
123
+ return Database.executeBatch(new {ClassName}(), DEFAULT_BATCH_SIZE);
124
+ }
125
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @description Domain class for {SObject}.
3
+ * Encapsulates field-level defaults, derivations, and validations.
4
+ * Operates only on in-memory SObject data — no SOQL or DML.
5
+ * @author Generated by Apex Class Writer Skill
6
+ */
7
+ public with sharing class {SObject}Domain {
8
+
9
+ // ─── Constants ───────────────────────────────────────────────────────
10
+ // TODO: Add constants for default values, statuses, etc.
11
+
12
+ // ─── Field Defaults ──────────────────────────────────────────────────
13
+
14
+ /**
15
+ * @description Applies default field values to new {SObject} records.
16
+ * Call this before insert to ensure consistent defaults.
17
+ * @param records List of {SObject} records to apply defaults to
18
+ */
19
+ public static void applyDefaults(List<{SObject}> records) {
20
+ if (records == null || records.isEmpty()) {
21
+ return;
22
+ }
23
+
24
+ for ({SObject} record : records) {
25
+ // TODO: Set default field values
26
+ // Example:
27
+ // if (record.Status__c == null) {
28
+ // record.Status__c = DEFAULT_STATUS;
29
+ // }
30
+ }
31
+ }
32
+
33
+ // ─── Derivations ────────────────────────────────────────────────────
34
+
35
+ /**
36
+ * @description Derives calculated field values based on other fields.
37
+ * Call this before insert and before update.
38
+ * @param records List of {SObject} records to derive values for
39
+ */
40
+ public static void deriveFields(List<{SObject}> records) {
41
+ if (records == null || records.isEmpty()) {
42
+ return;
43
+ }
44
+
45
+ for ({SObject} record : records) {
46
+ // TODO: Derive calculated field values
47
+ // Example:
48
+ // record.FullAddress__c = buildFullAddress(record);
49
+ }
50
+ }
51
+
52
+ // ─── Validations ────────────────────────────────────────────────────
53
+
54
+ /**
55
+ * @description Validates {SObject} records and adds errors for any violations.
56
+ * Call this before insert and before update.
57
+ * @param records List of {SObject} records to validate
58
+ */
59
+ public static void validate(List<{SObject}> records) {
60
+ if (records == null || records.isEmpty()) {
61
+ return;
62
+ }
63
+
64
+ for ({SObject} record : records) {
65
+ // TODO: Add validation rules
66
+ // Example:
67
+ // if (String.isBlank(record.Name)) {
68
+ // record.addError('Name is required.');
69
+ // }
70
+ }
71
+ }
72
+
73
+ // ─── Comparisons ────────────────────────────────────────────────────
74
+
75
+ /**
76
+ * @description Determines which fields have changed between old and new record versions.
77
+ * Useful in before update context.
78
+ * @param oldRecord The previous version of the record
79
+ * @param newRecord The current version of the record
80
+ * @return Set of field API names that have changed
81
+ */
82
+ public static Set<String> getChangedFields({SObject} oldRecord, {SObject} newRecord) {
83
+ Set<String> changedFields = new Set<String>();
84
+
85
+ if (oldRecord == null || newRecord == null) {
86
+ return changedFields;
87
+ }
88
+
89
+ Map<String, Schema.SObjectField> fieldMap = Schema.SObjectType.{SObject}.fields.getMap();
90
+ for (String fieldName : fieldMap.keySet()) {
91
+ if (oldRecord.get(fieldName) != newRecord.get(fieldName)) {
92
+ changedFields.add(fieldName);
93
+ }
94
+ }
95
+
96
+ return changedFields;
97
+ }
98
+
99
+ // ─── Private Helpers ─────────────────────────────────────────────────
100
+
101
+ // TODO: Add private helper methods as needed
102
+ }