$30 off During Our Annual Pro Sale. View Details »

Vader & the Army of Validators

Vader & the Army of Validators

A generic bean validation framework built & consumed by teams at Salesforce, to cater specific validation needs of Salesforce REST APIs. It abstracts away the validation complexity and promotes low-code config-based validation. This is built entirely using FOSS libraries and is about ripe to open-source.

Gopal S Akshintala

October 05, 2021
Tweet

More Decks by Gopal S Akshintala

Other Decks in Programming

Transcript

  1. Vader
    &


    The Army of


    Validators
    🦾
    🦾

    View Slide

  2. Why a new
    Framework?


    🤷
    @GopalAkshintala

    View Slide

  3. @GopalAkshintala
    Why a new
    Framework?
    Salesforce core has no de-facto
    standard for Bean validation
    Same Validations, Different
    execution strategy per context
    (like FailFast or ErrorAccumulate)
    No easy way to share validations
    among Beans that share common DS

    View Slide

  4. @GopalAkshintala
    If-else-try-catch-for
    Example🚨

    View Slide

  5. @GopalAkshintala
    Why a new
    Framework?
    Salesforce core has no de-facto
    standard for Bean validation
    Same Validations, Different
    execution strategies
    No easy way to share validations
    among Beans that share common DS

    View Slide

  6. @GopalAkshintala
    The Vision

    View Slide

  7. @GopalAkshintala

    View Slide

  8. @GopalAkshintala
    🧶 Enable them to Configure POJO/Bean Validations


    ⚙ Supply them the Algorithm for their Execution

    Strategy & DS (Like Fail-Fast for Batch)
    VadeR’s Business
    (Dev Teams as Customers)

    View Slide

  9. @GopalAkshintala
    Customers
    in
    Production
    4 Teams
    Rev Hydra
    Rev Delphinus
    Rev Centaurus
    Rev Pegasus
    In 3 different domains
    Payments
    Tax
    Billing

    View Slide

  10. @GopalAkshintala
    Presented
    @
    Confs
    🇺🇸 All Things Open, 2020, Raleigh, USA. 📹
    🏴󠁧󠁢󠁥󠁮󠁧󠁿 Kotlin User Group, London
    🇩🇪 Berlin Functional Programming Group
    🇳🇴 JavaBin, Norway
    🇩🇪 Kotlin User Group, Berlin
    🇮🇳 Google Developer Group Devfest 2019
    🇮🇳 Java User Group Hyderabad (@JUGHyd)
    🇮🇳 Salesforce, Hyderabad, India
    🇮🇳 Kotlin User Group, Hyderabad
    🇪🇸 JBCN Conf, 2020, Barcelona, Spain

    View Slide

  11. @GopalAkshintala
    Enough Sales
    Pitching!

    View Slide

  12. @GopalAkshintala
    We're Zealous about keeping the
    Dev-Experience Simple!

    View Slide

  13. @GopalAkshintala
    API for Non-Batch & Batch DS

    View Slide

  14. @GopalAkshintala
    Execution Strategies

    View Slide

  15. @GopalAkshintala
    Data-Structures to Validate

    View Slide

  16. Vader.validateAndFailFast(…): Optional
    @GopalAkshintala

    View Slide

  17. Vader.validateAndAccumulateErrors(…): List
    @GopalAkshintala

    View Slide

  18. @GopalAkshintala

    View Slide

  19. @GopalAkshintala
    2D Problem
    POJO/DS
    Validations

    View Slide

  20. VaderBatch.validateAndFailFastForEach(…): List>>
    @GopalAkshintala

    View Slide

  21. io.vavr.control.Either
    @GopalAkshintala
    interface Either {


    class Left implements Either


    class Right implements Either


    }

    View Slide

  22. Either
    Bean
    Validation Failure
    Sum-Type
    io.vavr.control.Either
    @GopalAkshintala
    Left Right

    View Slide

  23. VaderBatch.validateAndFailFastForAny(…): Optional
    @GopalAkshintala

    View Slide

  24. How to write
    Validators?
    @GopalAkshintala

    View Slide

  25. Validator: (ValidatableT)
    ->
    FailureT
    Validator validator =


    bean
    ->
    {


    if (bean
    ==
    null) {


    return new ValidationFailure(FIELD_NULL_OR_EMPTY);


    } else {


    return ValidationFailure.NONE;


    }


    };
    @GopalAkshintala

    View Slide

  26. Validator: (ValidatableT)
    ->
    FailureT
    static ValidationFailure validator(Bean bean) {


    if (bean
    ==
    null) {


    return new ValidationFailure(FIELD_NULL_OR_EMPTY);


    } else {


    return ValidationFailure.NONE;


    }


    }
    @GopalAkshintala

    View Slide

  27. @GopalAkshintala
    Consumer defines the FailureT
    Some examples of what a FailureT may include:
    Localised error message
    API error codes
    Exception handling

    View Slide

  28. How to pass these
    Validators


    into the API?
    @GopalAkshintala

    View Slide

  29. @GopalAkshintala
    🎛 Config for Validation

    View Slide

  30. @GopalAkshintala
    🎛 Config for Validation

    View Slide

  31. @GopalAkshintala
    Hook Validators with Config
    var config = ValidationConfig.toValidate()


    .withValidators(Tuple.of(validatorChain, ValidationFailure.NONE))


    .prepare();
    Validator validator1 = bean
    ->
    ValidationFailure.NONE;


    Validator validator2 = bean
    ->
    ValidationFailure.NONE;


    Validator validator3 = bean
    ->
    ValidationFailure.UNKNOWN_EXCEPTION;
    List>>
    validatorChain =


    List.of(validator1, validator2, validator3);
    Optional result = Vader.validateAndFailFast(Validatable, config);

    View Slide

  32. Config does more than hooking
    validators
    @GopalAkshintala
    Built-in
    Validators
    Plug-&-Play

    View Slide

  33. @GopalAkshintala
    Configure Mandatory fields
    Strict SF ID field validations
    Multi-Filter conditions to handle duplicates in a batch
    Min/Max batch size check with combination of multiple
    batch members
    Validators
    Plug-&-Play

    View Slide

  34. @GopalAkshintala
    Configure Mandatory Fields
    ValidationConfig.toValidate()


    .shouldHaveFieldsOrFailWithFn(Tuple.of(


    List.of(


    Bean
    ::
    getRequiredField1,


    Bean
    ::
    getRequiredField2,


    Bean
    ::
    getRequiredList),


    (missingFieldName, missingFieldValue)
    ->

    getFailureWithParams(REQUIRED_FIELD_MISSING, missingFldName, missingFldValue)))


    .prepare();

    View Slide

  35. @GopalAkshintala
    Configure Mandatory fields
    Strict SF ID field validations
    Multi-Filter conditions to handle duplicates in a batch
    Min/Max batch size check with combination of multiple
    batch members
    Validators
    Plug-&-Play

    View Slide

  36. @GopalAkshintala
    Configure SF ID Fields
    ValidationConfig.toValidate()


    .withIdConfig(


    IDConfig.toValidate()


    .withIdValidator(ValidIdUtil
    ::
    isThisEntity)


    .shouldHaveValidSFIdFormatForAllOrFailWithFn(Tuple.of(


    Map.of(


    BeanWithIdFields
    : :
    getAccountId, AccountUddConstants.EntityId,


    BeanWithIdFields
    : :
    getContactId, ContactUddConstants.EntityId),


    (invalidIdFldName, invalidIdFldValue)
    ->


    getFailureWithParams(INVALID_UDD_ID, invalidIdFldName, invalidIdFldValue))))


    .prepare();

    View Slide

  37. @GopalAkshintala
    Configure Mandatory fields
    Strict SF ID field validations
    Multi-Filter conditions to handle duplicates in a batch
    Min/Max batch size check with combination of multiple
    batch members
    Plug-&-Play
    Validators

    View Slide

  38. @GopalAkshintala
    Configure Multi-Filter for Batch
    Filter Criteria: Shouldn’t have same id1 OR id2

    View Slide

  39. @GopalAkshintala
    Configure Multi-Filter for Batch
    BatchValidationConfig.toValidate()


    .findAndFilterDuplicatesConfigs(List.of(


    FilterDuplicatesConfig.toValidate()


    .findAndFilterDuplicatesWith(id1Mapper)


    .andFailDuplicatesWith(DUPLICATE_ITEM_1),


    FilterDuplicatesConfig.toValidate()


    .findAndFilterDuplicatesWith(id2Mapper)


    .andFailDuplicatesWith(DUPLICATE_ITEM_2)))


    .prepare();

    View Slide

  40. @GopalAkshintala
    Configure Mandatory fields
    Strict SF ID field validations
    Multi-Filter conditions to handle duplicates in a batch
    Min/Max batch size check with combination of multiple
    batch members
    Specs
    Validators
    Plug-&-Play

    View Slide

  41. @GopalAkshintala
    Configure Batch-Size
    ContainerValidationConfig.toValidate()


    .withBatchMappers(List.of(


    ContainerWithMultiBatch
    ::
    getBatch1,


    ContainerWithMultiBatch
    ::
    getBatch2))


    .shouldHaveMinBatchSizeOrFailWith(Tuple.of(1, MIN_BATCH_SIZE_NOT_MET))


    .shouldHaveMaxBatchSizeOrFailWith(Tuple.of(10, MAX_BATCH_SIZE_EXCEEDED))


    .prepare();
    Criteria: 1
    <=
    size(batch1 + batch2)
    <=
    10

    View Slide

  42. @GopalAkshintala
    Configure Mandatory fields
    Strict SF ID field validations
    Multi-Filter conditions to handle duplicates in a batch
    Min/Max batch size check with combination of multiple
    batch members
    Specs
    Validators
    Plug-&-Play

    View Slide

  43. @GopalAkshintala
    Specs 🤓
    Low-code Validations
    spec._2()


    .when(Bean
    ::
    getStartDate)


    .then(Bean
    ::
    getEndDate)


    .shouldRelateWithFn(isOnOrBeforeIfBothArePresent())


    .orFailWith(ofFieldIntegrity(INVALID_START_AND_END_DATES))

    View Slide

  44. @GopalAkshintala
    Specs 🤓
    Low-code Validations
    ValidationConfig.toValidate()


    .specify(spec
    ->
    List.of(


    spec._2().


    ...
    ,


    spec._1(),


    ...
    ,


    . ..
    ))


    .prepare();

    View Slide

  45. @GopalAkshintala
    DSL Driven Config
    ValidationConfig.toValidate()


    .shouldHaveFieldsOrFailWithFn(…)


    .withIdConfig(…)


    .findAndFilterDuplicatesConfigs(…)


    .specify(…)


    .withValidators(…)


    .…


    .prepare();

    View Slide

  46. @GopalAkshintala
    You don’t need to write


    Tests for Config or


    Plug-&-Play Validators


    🙊

    View Slide

  47. @GopalAkshintala
    Think of it as a Type-Safe XML
    Config is NOT Code!

    View Slide

  48. @GopalAkshintala
    All Plug-&-Play Validators are
    Well-tested 🧪 with


    Unit Tests ✅

    View Slide

  49. @GopalAkshintala
    Nested Config


    for


    Beans with Nested DS

    View Slide

  50. @GopalAkshintala
    class Root {


    List containerBatch;


    }
    class Container {


    List memberBatch;


    }

    View Slide

  51. @GopalAkshintala
    BatchOfBatch1ValidationConfig.toValidate()


    .shouldHaveFieldOrFailWith(Item
    ::
    getId, INVALID_ITEM)


    .withMemberBatchValidationConfig(


    Tuple.of(Item
    :
    :
    getBeanBatch, memberBatchValidationConfig))


    .prepare();

    View Slide

  52. @GopalAkshintala
    🎛 Config based Validation


    Perks🍫
    Low Learning-curve
    Readability
    Maintainability
    Less Complexity
    Testability
    Flexibility
    Extensibility
    Re-usability/Sharing

    View Slide

  53. @GopalAkshintala
    Vader
    Not only for Advanced validation requirements
    It makes simple ones even Simpler!

    View Slide

  54. @GopalAkshintala
    Vader
    Current Applications
    Connect REST API Validations
    SObject Validation hooks
    FTests
    But it’s Generic!
    Independent of Consumer’s Implementation

    View Slide

  55. @GopalAkshintala
    Stable


    &


    Actively Maintained
    Vader

    View Slide

  56. @GopalAkshintala
    How to trust its


    Stability & Security?
    Vader

    View Slide

  57. @GopalAkshintala
    Vader
    Go Incremental 🧱

    View Slide

  58. @GopalAkshintala
    Vader
    How can I get my
    hands dirty?

    View Slide

  59. @GopalAkshintala
    Vader
    sfdc.co/vader
    Wanna Collab & Contribute?

    View Slide

  60. @GopalAkshintala
    bit.ly/vader-slides

    View Slide