Skip to content

Commit e4ceace

Browse files
authored
Merge branch 'master' into master
2 parents 0642891 + ca71952 commit e4ceace

File tree

6 files changed

+114
-56
lines changed

6 files changed

+114
-56
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Updates
1515
=======
1616

1717
- **December 2022**, **IMPORTANT CHANGE** - Support for native Apex User Mode was added to the library (see [discussion](https://github.com/apex-enterprise-patterns/fflib-apex-common/discussions/419)). For new projects, the old `enforceCRUD` and `enforceFLS` flags on `fflib_SObjectSelector` should be considered deprecated and the constructors that take `dataAccess` arguments should be used instead. Additionally, the introduction of `fflib_SObjectUnitOfWork.UserModeDML` provides an `IDML` implementation that supports `USER_MODE` or `SYSTEM_MODE`. `fflib_SObjectUnitOfWork.SimpleDML` (the default `IDML` implementation) should be considered deprecated. There are measurable performance benefits to using `SYSTEM_MODE` and `USER_MODE` (Apex CPU usage reduction). Additionally, the use of explicit `USER_MODE` and `SYSTEM_MODE` overrides the `with sharing` and `without sharing` class declaration and makes the expected behavior of DML and SOQL easier to understand.
18+
- **April 2021**, **IMPORTANT CHANGE**, the fflib_SObjectDomain has been split into a domain (fflib_IDomain) and triggerhandler (fflib_ISObjectDomain). This change can impact existing projects, please review [this page](docs/202105-new-domain-structure.md) for more details.
1819
- **April 2020**, **IMPORTANT CHANGE**, the directory format of this project repo was converted to [Salesforce DX Source Format](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_source_file_format.htm). While the GIT commit history was maintained, it is not visible on GitHub. If you need to see the history, either clone the repo and execute `git log --follow` from the command line or refer to this [tag](https://github.com/apex-enterprise-patterns/fflib-apex-common/tree/metadata-format-prior-to-dx-source-format-conversion) of the codebase prior to conversion.
1920
- **September 2014**, **IMPORTANT CHANGE**, changes applied to support Dreamforce 2014 advanced presentation, library now provides Application factories for major layers and support for ApexMocks. More details to follow! As a result [ApexMocks](https://github.com/apex-enterprise-patterns/fflib-apex-mocks) must be deployed to the org before deploying this library. The sample application [here](https://github.com/apex-enterprise-patterns/fflib-apex-common-samplecode) has also been updated to demonstrate the new features!
2021
- **July 2014**, **IMPORTANT CHANGE**, prior **23rd July 2014**, both the ``fflib_SObjectDomain.onValidate()`` and ``fflib_SObjectDomain.onValidate(Map<Id, SObject> existingRecords)`` methods where called during an on **after update** trigger event. From this point on the ``onValidate()`` method will only be called during on **after insert**. If you still require the orignal behaviour add the line ``Configuration.enableOldOnUpdateValidateBehaviour();`` into your constructor.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# New domain structure
2+
The new domain structure allows more flexibility of the object type a domain can contain.
3+
This allows for the creation of compound-domains and domains of data-classes.
4+
5+
It also splits the functionality into a Domain Model [as described by Martin Fowler](https://www.martinfowler.com/eaaCatalog/domainModel.html) and a separate trigger handler.
6+
This helps to structure the code better and makes it more clear which Apex code should be in the domain and which in the trigger handler.
7+
8+
9+
10+
## Previous interface and implementation structure
11+
| Interfaces | Implementation | Description |
12+
|:---|:---|:---|
13+
| fflib_ISObjectDomain | fflib_SObjectDomain | Used as Domain and trigger handler
14+
15+
16+
## New interface and implementation structure
17+
| Interfaces | Implementation | Description |
18+
|:---|:---|:---|
19+
| fflib_IDomain | | Generic identifier for domains
20+
| fflib_IObjects | fflib_Objects | Domains constructed with Objects, e.g. data-classes
21+
| fflib_ISObjects | fflib_SObjects | Domain containing SObjectTypes, e.g. Accounts, Contacts
22+
| fflib_ISObjectDomain | fflib_SObjectDomain | Used for trigger handlers and for legacy domains
23+
24+
See [this PR](https://github.com/apex-enterprise-patterns/fflib-apex-common/pull/300) for a detailed overview
25+
of all the code changes which were part of this change.
26+
27+
The [fflib-apex-common-samplecode](https://github.com/apex-enterprise-patterns/fflib-apex-common-samplecode)
28+
also includes examples on how to structure the change into the
29+
[new domain](https://github.com/apex-enterprise-patterns/fflib-apex-common-samplecode/blob/master/sfdx-source/apex-common-samplecode/main/classes/domains/Accounts.cls)
30+
and [trigger handler](https://github.com/apex-enterprise-patterns/fflib-apex-common-samplecode/blob/master/sfdx-source/apex-common-samplecode/main/classes/triggerHandlers/OpportunitiesTriggerHandler.cls)
31+
32+
## Known issues and how to resolve them
33+
34+
35+
### _Issue:_ Ambiguous method signature: void setMock(MyDomainClass)
36+
This happens when you try to mock an old domain class which is extended from fflib_SObjectDomain.
37+
```apex
38+
Application.Domain.setMock(mockAssetsDomain); // <<== generates Ambiguous method signature: void setMock
39+
```
40+
The issue can be resolved by casting the mock implementation to fflib_ISObjectDomain:
41+
> Application.Domain.setMock( **(fflib_ISObjectDomain)** mockAssetsDomain);
42+
43+
[See this issue report for more information](https://github.com/apex-enterprise-patterns/fflib-apex-common/issues/347)
44+
45+
46+
### _Issue:_ Illegal assignment from fflib_Domain to fflib_ISObjectDomain
47+
The `newInstance` method signature of the Application Domain Factory (fflib_Application.DomainFactory) has changed into:
48+
>public **fflib_IDomain** newInstance(***);
49+
50+
If you have:
51+
```apex
52+
fflib_ISObjectDomain domain = Application.Domain.newInstance(sObjIds);
53+
```
54+
You need to change that into:
55+
> fflib_ISObjectDomain domain = **(fflib_ISObjectDomain)** Application.Domain.newInstance(sObjIds);
56+
57+
[See this issue report for more information](https://github.com/apex-enterprise-patterns/fflib-apex-common/issues/346)

sfdx-source/apex-common/main/classes/fflib_Application.cls

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public virtual class fflib_Application
8686
* if set via the setMock method
8787
*
8888
* @remark If mock is set, the list of SObjectType in the mock could be different
89-
* then the list of SObjectType specified in this method call
89+
* than the list of SObjectType specified in this method call
9090
**/
9191
public virtual fflib_ISObjectUnitOfWork newInstance(List<SObjectType> objectTypes)
9292
{
@@ -102,7 +102,7 @@ public virtual class fflib_Application
102102
* if set via the setMock method
103103
*
104104
* @remark If mock is set, the list of SObjectType in the mock could be different
105-
* then the list of SObjectType specified in this method call
105+
* than the list of SObjectType specified in this method call
106106
**/
107107
public virtual fflib_ISObjectUnitOfWork newInstance(List<SObjectType> objectTypes, fflib_SObjectUnitOfWork.IDML dml)
108108
{
@@ -139,7 +139,7 @@ public virtual class fflib_Application
139139
* Note that this will not check the Apex Classes given actually implement the interfaces
140140
* as this information is not presently available via the Apex runtime
141141
*
142-
* @param serviceInterfaceTypeByServiceImplType Map ofi interfaces to classes
142+
* @param serviceInterfaceTypeByServiceImplType Map of interfaces to classes
143143
**/
144144
public ServiceFactory(Map<Type, Type> serviceInterfaceTypeByServiceImplType)
145145
{
@@ -189,11 +189,11 @@ public virtual class fflib_Application
189189
public SelectorFactory() { }
190190

191191
/**
192-
* Consturcts a Selector Factory linking SObjectType's with Apex Classes implement the fflib_ISObjectSelector interface
192+
* Constructs a Selector Factory linking SObjectType's with Apex Classes implement the fflib_ISObjectSelector interface
193193
* Note that the factory does not check the given Apex Classes implement the interface
194194
* currently this is not possible in Apex.
195195
*
196-
* @Param sObjectBySelectorType Map of SObjectType's to Selector Apex Classes
196+
* @param sObjectBySelectorType Map of SObjectType's to Selector Apex Classes
197197
**/
198198
public SelectorFactory(Map<SObjectType, Type> sObjectBySelectorType)
199199
{

sfdx-source/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ public interface fflib_ISObjectUnitOfWork
215215
**/
216216
void commitWork();
217217
/**
218-
* Register a generic peace of work to be invoked during the commitWork phase
218+
* Register a generic piece of work to be invoked during the commitWork phase
219219
*
220220
* @param work Work to be registered
221221
**/

sfdx-source/apex-common/main/classes/fflib_StringBuilder.cls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
/**
2828
* Helper class, roughly based on the Java version, but subclassed to assist in a number of use cases in this library
2929
*
30-
* NOTE: Aspects of this where developed before recent improvements to String handling, as such could likely be enhanced at this stage.
30+
* NOTE: Aspects of this were developed before recent improvements to String handling, as such could likely be enhanced at this stage.
3131
**/
3232
public virtual class fflib_StringBuilder
3333
{

sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -709,27 +709,27 @@ private with sharing class fflib_SObjectSelectorTest
709709
static void toSOQL_When_PolymorphicSelect_Expect_RelatedType() {
710710
//Given
711711

712-
CampaignMemberSelector cmSelector = new CampaignMemberSelector(fflib_SObjectSelector.DataAccess.LEGACY);
713-
fflib_QueryFactory qf = cmSelector.newQueryFactory();
714-
new LeadSelector().configureQueryFactoryFields(qf, 'Lead');
715-
new UserSelector().configureQueryFactoryFields(qf, 'Lead.Owner');
712+
CaseCommentSelector ccSelector = new CaseCommentSelector(fflib_SObjectSelector.DataAccess.LEGACY);
713+
fflib_QueryFactory qf = ccSelector.newQueryFactory();
714+
new CaseSelector().configureQueryFactoryFields(qf, 'Parent');
715+
new UserSelector().configureQueryFactoryFields(qf, 'Parent.Owner');
716716

717717

718718
Set<String> expectedSelectFields = new Set<String>{
719-
'Id', 'Status', 'Lead.Id', 'Lead.OwnerId', 'Lead.Owner.Id', 'Lead.Owner.UserRoleId'
719+
'Id', 'CommentBody', 'Parent.Id', 'Parent.OwnerId', 'Parent.Owner.Id', 'Parent.Owner.UserRoleId'
720720
};
721721
if (UserInfo.isMultiCurrencyOrganization()) {
722722
expectedSelectFields.add('CurrencyIsoCode');
723-
expectedSelectFields.add('Lead.CurrencyIsoCode');
724-
expectedSelectFields.add('Lead.Owner.CurrencyIsoCode'); // Because the Selector is for User; Group would not have
723+
expectedSelectFields.add('Parent.CurrencyIsoCode');
724+
expectedSelectFields.add('Parent.Owner.CurrencyIsoCode'); // Because the Selector is for User; Group would not have
725725
}
726726

727727
//When
728728
String soql = qf.toSOQL();
729729

730730
//Then
731-
Pattern soqlPattern = Pattern.compile(String.format('SELECT (.*) FROM CampaignMember ORDER BY {0} ASC NULLS FIRST ',
732-
new List<String>{cmSelector.getOrderBy()}));
731+
Pattern soqlPattern = Pattern.compile(String.format('SELECT (.*) FROM CaseComment ORDER BY {0} ASC NULLS FIRST ',
732+
new List<String>{ccSelector.getOrderBy()}));
733733
Matcher soqlMatcher = soqlPattern.matcher(soql);
734734
soqlMatcher.matches();
735735

@@ -741,29 +741,29 @@ private with sharing class fflib_SObjectSelectorTest
741741
static void toSOQL_When_PolymorphicSelectInMulticurrency_Expect_RelatedType() {
742742
//Given
743743

744-
CampaignMemberSelector cmSelector = new CampaignMemberSelector(fflib_SObjectSelector.DataAccess.LEGACY);
745-
fflib_QueryFactory qf = cmSelector.newQueryFactory();
746-
new LeadSelector().configureQueryFactoryFields(qf, 'Lead');
747-
new GroupSelector().configureQueryFactoryFields(qf, 'Lead.Owner');
744+
CaseCommentSelector ccSelector = new CaseCommentSelector(fflib_SObjectSelector.DataAccess.LEGACY);
745+
fflib_QueryFactory qf = ccSelector.newQueryFactory();
746+
new CaseSelector().configureQueryFactoryFields(qf, 'Parent');
747+
new GroupSelector().configureQueryFactoryFields(qf, 'Parent.Owner');
748748

749749

750750
Set<String> expectedSelectFields = new Set<String>{
751-
'Id', 'Status', 'Lead.Id', 'Lead.OwnerId', 'Lead.Owner.Id'
751+
'Id', 'CommentBody', 'Parent.Id', 'Parent.OwnerId', 'Parent.Owner.Id'
752752
};
753753
Set<String> unexpectedSelectFields = new Set<String>();
754754
if (UserInfo.isMultiCurrencyOrganization()) {
755755
expectedSelectFields.add('CurrencyIsoCode');
756-
expectedSelectFields.add('Lead.CurrencyIsoCode');
756+
expectedSelectFields.add('Parent.CurrencyIsoCode');
757757

758-
unexpectedSelectFields.add('Lead.Owner.CurrencyIsoCode'); // Because Group does NOT have CurrencyIsoCode
758+
unexpectedSelectFields.add('Parent.Owner.CurrencyIsoCode'); // Because Group does NOT have CurrencyIsoCode
759759
}
760760

761761
//When
762762
String soql = qf.toSOQL();
763763
System.debug(soql);
764764

765765
//Then
766-
Pattern soqlPattern = Pattern.compile('SELECT (.*) FROM CampaignMember ORDER BY CreatedDate ASC NULLS FIRST ');
766+
Pattern soqlPattern = Pattern.compile('SELECT (.*) FROM CaseComment ORDER BY CreatedDate ASC NULLS FIRST ');
767767
Matcher soqlMatcher = soqlPattern.matcher(soql);
768768
soqlMatcher.matches();
769769

@@ -780,30 +780,30 @@ private with sharing class fflib_SObjectSelectorTest
780780

781781
@IsTest
782782
static void toSOQL_When_SystemModePolymorphicSelect_Expect_RelatedType() {
783-
CampaignMemberSelector cmSelector = new CampaignMemberSelector(fflib_SObjectSelector.DataAccess.SYSTEM_MODE);
784-
fflib_QueryFactory qf = cmSelector.newQueryFactory();
785-
new LeadSelector().configureQueryFactoryFields(qf, 'Lead');
786-
new UserSelector().configureQueryFactoryFields(qf, 'Lead.Owner');
783+
CaseCommentSelector ccSelector = new CaseCommentSelector(fflib_SObjectSelector.DataAccess.SYSTEM_MODE);
784+
fflib_QueryFactory qf = ccSelector.newQueryFactory();
785+
new CaseSelector().configureQueryFactoryFields(qf, 'Parent');
786+
new UserSelector().configureQueryFactoryFields(qf, 'Parent.Owner');
787787

788788
List<String> expectedSelectFields = new List<String>();
789789
expectedSelectFields.add('id');
790-
expectedSelectFields.add('status');
790+
expectedSelectFields.add('commentbody');
791791
if (UserInfo.isMultiCurrencyOrganization()) {
792792
expectedSelectFields.add('currencyisocode');
793793
}
794-
expectedSelectFields.add('lead.ownerid');
795-
expectedSelectFields.add('lead.id');
794+
expectedSelectFields.add('parent.ownerid');
795+
expectedSelectFields.add('parent.id');
796796
if (UserInfo.isMultiCurrencyOrganization()) {
797-
expectedSelectFields.add('lead.currencyisocode');
797+
expectedSelectFields.add('parent.currencyisocode');
798798
}
799-
expectedSelectFields.add('lead.owner.userroleid');
800-
expectedSelectFields.add('lead.owner.id');
799+
expectedSelectFields.add('parent.owner.userroleid');
800+
expectedSelectFields.add('parent.owner.id');
801801
if (UserInfo.isMultiCurrencyOrganization()) {
802-
expectedSelectFields.add('lead.owner.currencyisocode');
802+
expectedSelectFields.add('parent.owner.currencyisocode');
803803
}
804804

805-
String expectedSOQL = String.format('SELECT ' + String.join(expectedSelectFields,', ') + ' FROM CampaignMember WITH SYSTEM_MODE ORDER BY {0} ASC NULLS FIRST ',
806-
new List<String>{cmSelector.getOrderBy()});
805+
String expectedSOQL = String.format('SELECT ' + String.join(expectedSelectFields,', ') + ' FROM CaseComment WITH SYSTEM_MODE ORDER BY {0} ASC NULLS FIRST ',
806+
new List<String>{ccSelector.getOrderBy()});
807807

808808
//When
809809
String actualSOQL = qf.toSOQL();
@@ -843,70 +843,70 @@ private with sharing class fflib_SObjectSelectorTest
843843
Assert.areEqual(expected,aQF.toSOQL());
844844
}
845845

846-
private class CampaignMemberSelector extends fflib_SObjectSelector {
847-
public CampaignMemberSelector(DataAccess access) {
846+
private class CaseCommentSelector extends fflib_SObjectSelector {
847+
public CaseCommentSelector(DataAccess access) {
848848
super(false, access);
849849
}
850850

851851
public List<Schema.SObjectField> getSObjectFieldList() {
852852
return new List<Schema.SObjectField>{
853-
CampaignMember.Id,
854-
CampaignMember.Status
853+
CaseComment.Id,
854+
CaseComment.CommentBody
855855
};
856856
}
857857

858858
public Schema.SObjectType getSObjectType() {
859-
return CampaignMember.sObjectType;
859+
return CaseComment.sObjectType;
860860
}
861861
}
862862

863-
private class UserSelector extends fflib_SObjectSelector {
864-
public UserSelector() {
863+
private class CaseSelector extends fflib_SObjectSelector {
864+
public CaseSelector() {
865865
super();
866866
}
867867

868868
public List<Schema.SObjectField> getSObjectFieldList() {
869869
return new List<Schema.SObjectField>{
870-
User.UserRoleId,
871-
User.Id
870+
Case.OwnerId,
871+
Case.Id
872872
};
873873
}
874874

875875
public Schema.SObjectType getSObjectType() {
876-
return User.sObjectType;
876+
return Case.sObjectType;
877877
}
878878
}
879879

880-
private class GroupSelector extends fflib_SObjectSelector {
881-
public GroupSelector() {
880+
private class UserSelector extends fflib_SObjectSelector {
881+
public UserSelector() {
882882
super();
883883
}
884884

885885
public List<Schema.SObjectField> getSObjectFieldList() {
886886
return new List<Schema.SObjectField>{
887-
Group.Id
887+
User.UserRoleId,
888+
User.Id
888889
};
889890
}
890891

891892
public Schema.SObjectType getSObjectType() {
892-
return Group.sObjectType;
893+
return User.sObjectType;
893894
}
894895
}
895896

896-
private class LeadSelector extends fflib_SObjectSelector {
897-
public LeadSelector() {
897+
private class GroupSelector extends fflib_SObjectSelector {
898+
public GroupSelector() {
898899
super();
899900
}
900901

901902
public List<Schema.SObjectField> getSObjectFieldList() {
902903
return new List<Schema.SObjectField>{
903-
Lead.OwnerId,
904-
Lead.Id
904+
Group.Id
905905
};
906906
}
907907

908908
public Schema.SObjectType getSObjectType() {
909-
return Lead.sObjectType;
909+
return Group.sObjectType;
910910
}
911911
}
912912
}

0 commit comments

Comments
 (0)