Any structure expected to stand the test of time and change needs a strong foundation! Software is no exception. Engineering your code to grow in a stable and effective way is critical to your ability to rapidly meet the growing demands of users, new features, technologies, and platform capabilities. Join us to obtain architect-level design patterns for use in your Apex code to keep it well factored, easy to maintain, and in line with platform best practices. You'll follow a Force.com interpretation of Martin Fowler's Enterprise Architecture Application patterns, and the practice of Separation of Concerns.
2. All about FinancialForce.com
Revolutionizing the Back Office
#1 Accounting, Billing and PSA Apps on the Salesforce platform
▪ Native apps
▪ San Francisco HQ, 595 Market St
▪ R&D in San Francisco, Harrogate UK, and Granada ES
▪ We are hiring! Meet us at Rehab!
4. What's wrong with this picture?
public with sharing class MyController
{
public String theId;
public MyController(ApexPages.StandardController standardController)
{
theId = (String) ApexPages.currentPage().getParameters().get('id');
}
public PageReference actionCheckInvoices()
{
// Read the Account record
Account accountRecord = [select Id, Name from Account where Id = :theId];
// Do some processing on the Account record ... then update it
update accountRecord;
return null;
}
}
public with sharing class MyBatchJob implements Database.Batchable<SObject>
{
public void execute(Database.batchableContext info, List<Account> accounts)
{
for(Account account : accounts)
{
MyController myController = new MyController(new ApexPages.StandardController(account));
myController.actionCheckInvoices();
}
}
public Database.QueryLocator start(Database.BatchableContext info) { ... }
public void finish(Database.batchableContext info) { ... }
}
Developer “A” writes an
Apex Controller first
Developer “B” writes an
Apex Batch job later.
5. So what was wrong with that picture?
MyController
MyController
Issue
Use of ApexPages.currentPage()
Unnecessary and fragile, utilize instead
stdController.getId() method
Error handling
No try/catch error handling on controller
method
Developer “A”
MyBatchJob
Issue
Use of ApexPages.currentPage()
Is not available in a Batch Apex context, the constructor
code will give an exception.
SOQL Query and DML Governors
Calling the controller method in a loop will cause a SOQL
query and DML governor issues.
Error Handling
No try/catch error handling in the the execute method
Separation of Concerns
Developer A did not originally develop the controller logic
expecting or anticipating Developer B’s would in the future
try to reuse it from a Batch Context as well.
They did not consider correct Separation of Concerns…
Developer “B”
7. So what is “Separation of Concerns” then?
“The goal is to design systems so that functions can be
optimized independently of other functions, so that failure of one
function does not cause other functions to fail, and in general to
make it easier to understand, design and manage complex
interdependent systems”
Wikipedia, “Separation of Concerns”
8. So what is “DRY” then?
Don’t Repeat Yourself
“Every piece of knowledge must have a single, unambiguous,
authoritative representation within a system.”
Wikipedia, “Don’t repeat yourself (DRY)”
9. Design Patterns for SOC & Force.com Best Practice
Service Layer
Domain Layer
▪ Yet another Wrapper / Trigger pattern!
Selector Layer
Reference Martin Fowler
▪
http://martinfowler.com/eaaCatalog/
▪
Author of “Patterns of Enterprise
Application Architecture”
Reference
▪
http://wiki.developerforce.com/page/Apex_Enterprise_Patterns_-_Separation_of_Concerns
10. Demo! “takeaway” a Sample Force.com Application!
GitHub: financialforcedev/fflib-apex-common-samplecode
11. Sample Application, what’s on the menu?
Platform Feature
Patterns Used
Custom Buttons
Building UI logic and calling Service Layer code
from Controllers
Batch Apex
Reusing Service and Selector Layer code from
with a Batch context
Integration API
Exposing an Integration API via Service Layer
using Apex and REST
Apex Triggers
Factoring your Apex Trigger logic via the Domain
Layer (wrappers)
VisualForce Remoting
Exposing Service Layer code to HTML5 /
JavaScript libraries such as JQuery
GitHub: financialforcedev/fflib-apex-common-samplecode
14. Introducing the Service Layer
Naming Convention
▪ Suffix with ‘Service’, e.g. OpportunitiesService
▪ Methods named by purpose not usage, e.g. applyDiscounts
15. Introducing the Service Layer
Clear Code Factoring
▪ Encapsulates Processes / Tasks
▪ Caller Context Agnostic
16. Introducing the Service Layer
Defined Responsibilities
▪ Supports Bulkifcation
▪ Transaction management
17. Developing and calling a Service Layer
public with sharing class AccountService
{
public static void checkOutstandingInvoices(Set<Id> accountIds)
{
// Read the Account records
List<Account> accountRecords = [select Id, Name from Account where Id in :accountIds];
// Do some processing on the Account and Invoice records ... then update them.
update accountRecords;
}
}
Developer “A”
follows Service
Layer pattern.
AccountService.cls
Service Contract
public PageReference actionCheckInvoices()
{
try {
// Process the Account record
AccountService.checkOutstandingInvoices(new Set<Id> { theId });
} catch (Exception e) { ApexPage.addMessages(e); }
return null;
}
Developer “A”
writes Controller
code to consume
the Service.
MyController.cls
public void execute(Database.batchableContext info, List<Account> accounts)
{
try {
// Process Account records
Set<Id> accountIds = new Map<Id, Account>(accounts).keySet();
AccountService.checkOutstandingInvoices(accountIds);
} catch (Exception e) { emailError(e); }
}
MyBatch.cls
Developer “B”
then reuses the
Developer “A”
code safely.
18. Code Walkthrough : Sample Services
Managing DML and Transactions
▪ Unit of Work Pattern
Classes
OpportunitiesService.cls
19. Code Walkthrough : Custom Buttons
Custom Buttons
▪
Detail and List View
▪
Calling Visualforce Controller Code
Visualforce Controllers and Pages
▪
Error Handling
▪
Interacts with Service Layer
•
Utilize bulkified methods
•
Assume transaction containment
•
Catch exceptions and display them on the page
Classes and Pages
OpportunityApplyDiscountController.cls
• opportunityapplydiscount.page
• opportunityapplydiscounts.page
OpportunityCreateInvoiceController.cls
• opportunitycreateinvoice.page
• opportunitycreateinvoices.page
20. CodeHandling
Walkthrough : Batch Apex
▪ Error
Classes
▪ Interacts with Service Layer
CreatesInvoicesJob.cls
• Utilize bulkified methods
• Assume transaction containment
• Catch exceptions and logs them for later notificaiton
21. CodeHandling
Walkthrough : Exposing an Integration API
▪ Error
Classes
• Pass business logic exceptions to caller to handle
• Assume transaction containment
OpportunitiesResource.cls
OpportunitiesService.cls
▪ Exposing the API
• To Apex Developers > Mark Service class and methods as global!
–
Package to apply Apex versioning of the API
• To Web / Mobile Developers > Create RESTful API around Service layer
22. Code Walkthrough : Visualforce Remoting
An “Opportunities Discount Console”
Classes
OpportunityConsoleController.cls
▪ Developer uses a Rich HTML5 Client Library, such as JQuery
• Is not using traditional Visualforce markup or action functions on the controller
▪ Utilizing static “remote” methods on the controller (stateless controller)
• Able to consume the Service layer functions also!
• Assumes transactional integrity
• Assumes the caller (JavaScript client) to handle exceptions
25. Introducing the Domain Layer
Naming Convention
▪ Name uses plural name of object, e.g. Opportunities
26. Introducing the Domain Layer
Clear Code Factoring
▪ Encapsulates Validation
/ Defaulting of Fields
▪ Wraps Apex Trigger Logic
in Apex Class
(Trigger/Wrapper Pattern)
27. Introducing the Domain Layer
Defined Responsibilities
▪ Enforces Bulkifcation for logic
▪ Platform security best practice
(honors Users profile)
▪ Encapsulates ALL logic / behavior
for each object
• e.g. onValidate, onBeforeInsert and
applyDiscount
32. Code Walkthrough : Domain : Extends vs Interfaces
Extending Apex Domain Classes
▪ Extend or adjust existing behavior of classes of a similar nature
• Base class Chargeable ensures cost is recalculated on insert and update
• DeveloperWorkItems Domain Class extends to provide its own calc logic
Implementing Apex Domain Interfaces
▪ Good for applying behavior to different types of classes
• Interface InvoiceService.ISupportInvoicing
• Implemented by Opportunities Domain Class
33. Code Walkthrough : Domain Class Extension
Chargeable Base Class
▪ Apply to Custom Objects recording Work
▪ Require hours and cost calculations
▪ Performs recalculation on insert and update
38. Introducing the Selector Layer
Defined Responsibilities
▪ Consistency over queried fields
▪ Platform security best practice
▪ Encapsulates ALL query logic
39. Code Walkthrough : Selector Layer
Encapsulate Query Fields and Logic
▪ Base class fflib_SObjectSelector
▪ Defines Common Fields
▪ Default SOQL Helpers, FieldSet Support
Apex Classes
OpportunitiesSelector.cls
OpportunityLineItemsSelector.cls
ProductsSelector.cls
PricebooksSelector.cls
PricebookEntriesSelector.cls
40. Code Walkthrough : Selector Layer : Callers
Service Layer Logic : OpportunitiesSelector.selectByIdWithProducts
Apex Class
OpportunitiesService.cls
Domain Layer Logic : AccountsSelector.selectByOpportunity
Apex Class
Opportunities.cls
45. When is SOC / DRY appropriate (a rough guide)?
Solution / Code
Base Size
Developers
Requirements Scope
Number of Client Types
and Interactions
SOC/DRY
Appropriate?
Small
1 to 2
•
Well known and unlikely to
change
One off solutions
Limited number of objects
•
•
•
•
•
Standard UI
Simple VF / Triggers
No Batch Mode
No API
No Mobile
Typically not
Well known but may need to
evolve rapidly
Growing number objects and
processes interacting
Product deliverable or larger
duration projects
•
•
•
•
•
Standard UI
Advanced VF / JQuery
Batch Mode
API (on roadmap)
Mobile (on roadmap)
Worth
considering
Scope driven by multiple
customers and user types
Large number of objects
Generic product or solution
aimed at Mid to Enterprise
market with Customer or
Partner Integrations.
Growing development team!
•
•
•
•
•
•
Standard UI
Advanced VF / JQuery
Batch Mode
Developer / Partner API
Mobile Clients
New Platform Feature
Ready, Chatter Actions!
•
•
Small to Medium
1 to 6
•
•
•
Large
>6
•
•
•
•
Definite benifits