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

`Huh?` to `Aha!`: A Refactoring Story

`Huh?` to `Aha!`: A Refactoring Story

Blog post: https://overfullstack.ga/posts/huh-to-aha

## Abstract
**As a Dev, I need to Refactor, to make codebase Refactorable**. Let's achieve this goal through a least spoken, yet most effective Method - **Component Isolation**. Let's discuss how to overcome major **Obstacles** that hinder this goal, and **Metrics** that help measure our success.

## Audience & Take-aways
- This applies to software developers at all levels. I use **Java** to demonstrate the code snippets, but this talk is about Software Design and is agnostic of programming language.
- Refactoring is **NOT** moving lines of code around with IDE shortcuts. The **USP** for this talk is, it takes a unique approach to Refactoring i.e., Component Isolation and provides persuasive arguments against factors that cause __quantum-entanglement__ among components.
- Take-aways for the audiences are:
- How isolated components can build long-lasting structures, keeping rate of entropy growth in check.
- How to use **MOM** process (Methods, Obstacles and Metrics), to build a case for your Refactoring story and pitch it to your team and Manager for funding.
- How to eliminate __Exceptions__ from your code using **ADTs (Algebraic Data Types)**.
- How to replace __Shared Global mutable state on your Shared Code__ with **Immutability** and **Data Transformations**.
- Demo a hello-real-world service with these problems and how applying these simple methods can change the face of this service, thereby reducing the cognitive complexity and organically boosting the testability.
- Metric-driven approach to objectively realize the results and improvements achieved.

Gopal S Akshintala

June 21, 2021
Tweet

More Decks by Gopal S Akshintala

Other Decks in Programming

Transcript

  1. !
    H ?
    t
    A !
    A ef ct ri g to y

    View Slide

  2. I'm
    G pa S ks in al
    !
    S S e E r @ S r
    !
    @G s a
    !
    o l .g
    2

    View Slide

  3. W at’s ef ct ri g?
    @GopalAkshintala overfullstack.ga
    3

    View Slide

  4. W at’s ef ct ri g?
    @GopalAkshintala overfullstack.ga
    4

    View Slide

  5. A a ev, I ee t
    R fa to , t m ke od ba e
    ef ct ra le
    @GopalAkshintala overfullstack.ga
    5

    View Slide

  6. @GopalAkshintala overfullstack.ga
    6

    View Slide

  7. H w o it h ou S or
    f r un in ?
    @GopalAkshintala overfullstack.ga
    7

    View Slide

  8. M th ds
    O st cl s
    M tr cs
    @GopalAkshintala overfullstack.ga
    8

    View Slide

  9. M th ds
    !
    Human Readability
    !
    Component Isolation
    @GopalAkshintala overfullstack.ga
    9

    View Slide

  10. O st cl s
    !
    Exceptions
    !
    Mutability
    @GopalAkshintala overfullstack.ga
    10

    View Slide

  11. M tr cs
    !
    Cognitive Complexity
    !
    Testability
    @GopalAkshintala overfullstack.ga
    11

    View Slide

  12. Methods Metrics
    ! Human Readability " Cognitive Complexity
    ! Component Isolation " Testability
    Obstacles
    ! Exceptions
    ! Mutability
    @GopalAkshintala overfullstack.ga
    12

    View Slide

  13. L t's ta t it o r
    O st cl s
    @GopalAkshintala overfullstack.ga
    13

    View Slide

  14. O st cl - 1
    E ce ti ns
    @GopalAkshintala overfullstack.ga
    14

    View Slide

  15. E ce ti ns re he os A us d
    l ng ag f at re
    Catch or Rethrow anything
    try {
    !!"
    } catch(NullPointerException | IllegalArgumentException | IndexOutOfBoundsException e) {
    e.printStackTrace();
    }
    try {
    !!"
    } catch(Exception e) {
    throw e;
    }
    @GopalAkshintala overfullstack.ga
    15

    View Slide

  16. O st cl - 1 - E ce ti ns
    E ce ti ns re or ho es
    !
    @GopalAkshintala overfullstack.ga
    16

    View Slide

  17. I Es
    @GopalAkshintala overfullstack.ga
    17

    View Slide

  18. E ce ti ns re or ho es
    Tightly coupled to not just the caller, but the entire call hierarchy
    void verifyUserAccess(String userId) throws NoAccessException {
    if (!hasAccess1(userId)) {
    throw new NoAccessException(ACCESS_1);
    }
    if (!hasAccess2(userId)) {
    throw new NoAccessException(ACCESS_2);
    }
    if (!hasAccess3(userId)) {
    throw new NoAccessException(ACCESS_3);
    }
    }
    @GopalAkshintala overfullstack.ga
    18

    View Slide

  19. I’m so at d
    !
    Optional verifyUserAccess(String userId) {
    if (!hasAccess1(userId)) {
    return Optional.of(new NoAccessException(ACCESS_1));
    }
    if (!hasAccess2(userId)) {
    return Optional.of(new NoAccessException(ACCESS_2));
    }
    if (!hasAccess3(userId)) {
    return Optional.of(new NoAccessException(ACCESS_3));
    }
    return Optional.empty();
    }
    @GopalAkshintala overfullstack.ga
    19

    View Slide

  20. R pl ce xc pt on w th DT
    ‣ Optional - some OR none
    ‣ Tuple (VAVR) - 1 WITH/AND 2
    ‣ Either (VAVR) - left OR right
    ‣ With the advent of Pattern Matching in Java 16+, these
    blend more naturally!
    @GopalAkshintala overfullstack.ga
    20

    View Slide

  21. E ce ti ns is se t r tu n ul ip e
    D ta yp s
    int parse(String s) {
    if (s.matches("-?[0-9]+")) {
    return Integer.parseInt(s);
    }
    throw new NumberFormatException("Not a valid integer");
    }
    @GopalAkshintala overfullstack.ga
    21

    View Slide

  22. R pl ce xc pt on it E th r
    !" Before
    int parse(String s) {
    if (s.matches("-?[0-9]+")) {
    return Integer.parseInt(s);
    }
    throw new NumberFormatException("Not a valid integer");
    }
    !" After
    Either parse(String s) {
    if (s.matches("-?[0-9]+")) {
    return Either.right(Integer.parseInt(s));
    }
    return Either.left(new NumberFormatException("Not a valid integer"));
    }
    @GopalAkshintala overfullstack.ga
    22

    View Slide

  23. O st cl - 1 - E ce ti ns
    T ro a ay xc pt on
    @GopalAkshintala overfullstack.ga
    23

    View Slide

  24. O st cl - 2
    M ta le ta e
    @GopalAkshintala overfullstack.ga
    24

    View Slide

  25. S ar d ut bl s at
    @GopalAkshintala overfullstack.ga
    25

    View Slide

  26. S ar d ut bl s at
    o a
    S ar d od ba e
    @GopalAkshintala overfullstack.ga
    26

    View Slide

  27. O st cl - 2 - M ta il ty
    M ta le bj ct a I pu
    P ra s ou le om on nt
    @GopalAkshintala overfullstack.ga
    27

    View Slide

  28. static int sum(List nums) {
    int result = 0;
    for (int num : nums) {
    result += num;
    }
    return result;
    }
    @GopalAkshintala overfullstack.ga
    28

    View Slide

  29. static int sumAbsolute(List nums) {
    for (int i = 0; i < nums.size(); i!") {
    nums.set(i, Math.abs(nums.get(i)));
    }
    return sum(nums); !# DRY
    }
    @GopalAkshintala overfullstack.ga
    29

    View Slide

  30. static void client() {
    var nums = Arrays.asList(-2, 5, -6);
    System.out.println(sumAbsolute(nums));
    }
    !
    GA release!
    "#
    @GopalAkshintala overfullstack.ga
    30

    View Slide

  31. static void client() {
    var nums = Arrays.asList(-2, 5, -6);
    System.out.println(sumAbsolute(nums));
    System.out.println(sum(nums)); !" 13
    }
    @GopalAkshintala overfullstack.ga
    31

    View Slide

  32. static int sumAbsolute(List nums) {
    for (int i = 0; i < nums.size(); i!") {
    nums.set(i, Math.abs(nums.get(i))); !# Latent Bug
    }
    return sum(nums); !# DRY
    }
    static void client() {
    List nums = Arrays.asList(-2, 5, -6);
    System.out.println(sumAbsolute(nums));
    System.out.println(sum(nums)); !#
    }
    @GopalAkshintala overfullstack.ga
    32

    View Slide

  33. O st cl - 2 - M ta il ty
    M ta le bj ct a i pu
    p ra s s nh ly f r
    so at on
    @GopalAkshintala overfullstack.ga
    33

    View Slide

  34. O st cl - 2 - M ta il ty
    H w bo t ut bl O je ts
    a et rn yp s?
    @GopalAkshintala overfullstack.ga
    34

    View Slide

  35. M ta le bj ct a R tu n yp s
    Date getEggLayingDate(int eggId) {
    !" heavy operation
    return queryEggLayingDateFromDB(eggId);
    }
    @GopalAkshintala overfullstack.ga
    35

    View Slide

  36. !" Dependent component - 1
    boolean isLaidInFirstHalf(int eggId) {
    var layingDate = getEggLayingDate(eggId);
    if (layingDate.getDate() < 15) {
    return true;
    }
    return false;
    }
    !" Dependent component - 2
    int calculateEggAge(int eggId, Date today) {
    return today.getDate() - getEggLayingDate(eggId).getDate();
    }
    @GopalAkshintala overfullstack.ga
    36

    View Slide

  37. O e ay!
    !" Dependent component - 1
    boolean isLaidInFirstHalf(int eggId) {
    var layingDate = getEggLayingDate(eggId);
    if (layingDate.getDate() < 15) {
    !" It's just logging, let's reuse the same Date obj for month and year
    layingDate.setDate(15);
    logger.info("This egg was laid before: " + layingDate);
    return true;
    }
    return false;
    }
    @GopalAkshintala overfullstack.ga
    37

    View Slide

  38. A ot er ay!
    static final Map eggLayingDateCacheById =
    new HashMap!"(); !# Cache
    Date getEggLayingDate(int eggId) {
    return eggLayingDateCacheById
    .computeIfAbsent(eggId, this!$queryEggLayingDateFromDB);
    }
    @GopalAkshintala overfullstack.ga
    38

    View Slide

  39. A ot er ay!
    !" Dependent component - 1
    boolean isLaidInFirstHalf1(int eggId) {
    var layingDate = getEggLayingDate(eggId);
    if (layingDate.getDate() < 15) {
    !" It's just logging, let's reuse the same Date obj for month and year
    layingDate.setDate(15); !" Mutation
    !
    logger.info("This egg was laid before: " + layingDate);
    return true;
    }
    return false;
    }
    !" Dependent component - 2
    long calculateEggAge(int eggId, Date today) {
    return today.getDate() - getEggLayingDate(eggId).getDate(); !" What did I do?
    }
    @GopalAkshintala overfullstack.ga
    39

    View Slide

  40. O st cl - 2 - M ta il ty
    T at’s ua tu
    E ta gl me t ⚛
    @GopalAkshintala overfullstack.ga
    40

    View Slide

  41. P in er E er wh re!
    @GopalAkshintala overfullstack.ga
    41

    View Slide

  42. O st cl - 2 - M ta il ty
    B t hy s ut bi it
    p ed mi an i J va od ?
    @GopalAkshintala overfullstack.ga
    42

    View Slide

  43. P we o D fa lt
    M ta il ty s he ef ul m de n av
    @GopalAkshintala overfullstack.ga
    43

    View Slide

  44. O st cl - 2 - M ta il ty
    M ke mm ta il ty ou
    D fa lt
    @GopalAkshintala overfullstack.ga
    44

    View Slide

  45. I t ke D sc pl ne o ea t e ef ul
    1
    1 Designed by rawpixel.com / Freepik
    @GopalAkshintala overfullstack.ga
    45

    View Slide

  46. S me ui k in
    !
    ‣ Make a habit to use final before var and function params.
    ‣ Follow Immutable strategy for POJOs from Oracle’s Documentation
    ‣ (Pre Java 16) Auto-generate Immutable version of your POJO
    using:
    ‣ Lombok (More Magic, Less Effort)
    ‣ Google Auto (Less Magic, More Effort)
    ‣ Immutables (Less Magic, More Effort)
    ‣ Use Record types from Java 16
    @GopalAkshintala overfullstack.ga
    46

    View Slide

  47. O st cl - 2 - M ta il ty
    W at o nt -I mu ab es
    s y?
    @GopalAkshintala overfullstack.ga
    47

    View Slide

  48. O st cl - 2 - M ta il ty - A ti-i mu ab es
    I n't mm ta il ty nl f r
    ul i-t re di g?
    @GopalAkshintala overfullstack.ga
    48

    View Slide

  49. I mu ab li y sn't nl f r
    C nc rr nc
    @GopalAkshintala overfullstack.ga
    49

    View Slide

  50. O st cl - 2 - M ta il ty - A ti-i mu ab es
    I mu ab e bj ct d es ’t
    t y mp ra iv s yl !
    @GopalAkshintala overfullstack.ga
    50

    View Slide

  51. M ta il ty & I pe at vi y re ri nd !
    @GopalAkshintala overfullstack.ga
    51

    View Slide

  52. M ta il ty ik s o e it
    I pe at vi y
    void mutableFn() {
    var mutableList = Arrays.asList("a", "b", "c");
    mutateList(mutableList);
    }
    List mutateList(List list) {
    for (var i = 0; i < list.size(); i!") {
    list.set(i, list.get(i).toUpperCase());
    }
    return list;
    }
    @GopalAkshintala overfullstack.ga
    52

    View Slide

  53. I pe at ve ut ti ns ep ac d y
    D cl ra iv T an fo ma io s
    void immutableFn() {
    final var immutableList = List.of("a", "b", "c");
    transformList(immutableList);
    }
    List transformList(final List list) {
    !" `toList()` is new in Java 16 to collect Stream into UnmodifiableList.
    return list.stream().map(String!#toUpperCase).toList();
    }
    @GopalAkshintala overfullstack.ga
    53

    View Slide

  54. I mu ab li y & T an fo ma io a e
    C up e!
    N C oi e!
    @GopalAkshintala overfullstack.ga
    54

    View Slide

  55. I mu ab li y or es ra sf rm ti n
    void immutableFn() {
    final var immutableList = List.of("a", "b", "c");
    !" ! Throws UnsupportedOperationException
    mutateList(immutableList);
    }
    @GopalAkshintala overfullstack.ga
    55

    View Slide

  56. O st cl - 2 - M ta il ty - A ti-i mu ab es
    D es 't mm ta il ty e t
    er ?
    @GopalAkshintala overfullstack.ga
    56

    View Slide

  57. I mu ab li y nd er or an e
    ctrl-c + ctrl-v from Oracle’s Documentation
    ‣ The impact of object creation is often overestimated.
    ‣ It can be offset by decreased overhead due to garbage
    collection.
    @GopalAkshintala overfullstack.ga
    57

    View Slide

  58. J va’s mb ac ng mm ta il ty, s ow y
    ‣ Most used Data type in any Java application?
    ‣ String - No coincidence that it’s Immutable.
    ‣ Java 8 replaced Date with immutable LocalDate.
    ‣ Java 11 introduced Immutable Collections.
    ‣ Java 16 introduced Record types, toList on Stream
    for UnmodifiableList
    ‣ And many moreG
    @GopalAkshintala overfullstack.ga
    58

    View Slide

  59. H ll R al or d
    @GopalAkshintala overfullstack.ga
    59

    View Slide

  60. @GopalAkshintala overfullstack.ga
    60

    View Slide

  61. @GopalAkshintala overfullstack.ga
    61

    View Slide

  62. @GopalAkshintala overfullstack.ga
    62

    View Slide

  63. H w as he od ?
    @GopalAkshintala overfullstack.ga
    63

    View Slide

  64. H w as he od ?
    !
    void filterDuplicates(Map failureMap,
    List eggsFromRequest) {!!"}
    void validate(Map failureMap,
    List nonDuplicateEggs) {!!"}
    List toEntityObjs(List validEggs) {!!"}
    void bulkInsertIntoDB(List eggEntityObjs)
    throws DMLOperationException {!!"}
    @GopalAkshintala overfullstack.ga
    64

    View Slide

  65. P rt ti n et ee S ep
    filterDuplicates(failureMap, eggsFromRequest);
    !!"
    !!"
    !# Partition `eggsFromRequest` using `failureMap`
    !# into `duplicateEggs`, `nonDuplicateEggs`
    !!"
    !!"
    validate(failureMap, nonDuplicateEggs);
    @GopalAkshintala overfullstack.ga
    65

    View Slide

  66. P rt ti n et ee S ep
    filterDuplicates(failureMap, eggsFromRequest);
    !" Partition
    var nonDuplicateEggs = new ArrayList();
    for (Egg egg : eggsFromRequest) {
    if (!failureMap.containsKey(egg.getId())) {
    nonDuplicateEggs.add(egg);
    }
    }
    validate(failureMap, nonDuplicateEggs);
    @GopalAkshintala overfullstack.ga
    66

    View Slide

  67. H w as t?
    !
    filterDuplicates(failureMap, eggsFromRequest);
    !!"
    !# Partition
    !!"
    validate(failureMap, nonDuplicateEggs);
    !!"
    !# Partition
    !!"
    var eggEntityObjs = toEntityObjs(validEggs);
    try {
    bulkInsertIntoDB(eggEntityObjs);
    } catch (DMLOperationException ex) {
    handlePartialFailures(failureMap, ex);
    }
    !# Prepare response from `failureMap` and db insertion results
    @GopalAkshintala overfullstack.ga
    67

    View Slide

  68. H w t's ef ct re i to
    !
    @GopalAkshintala overfullstack.ga
    68

    View Slide

  69. T e ig at re hi t
    !" Before
    void filterDuplicates(Map failureMap,
    List eggsFromRequest) {!!#}
    !" After
    static List, ImmutableEgg!$ filterDuplicates(
    final List eggsFromRequest) {!!#}
    @GopalAkshintala overfullstack.ga
    69

    View Slide

  70. U in E th r or ar ia F il re
    List, ImmutableEgg>>
    @GopalAkshintala overfullstack.ga
    70

    View Slide

  71. P rt al ai ur s ow s re m ↡
    List, ImmutableEgg>>
    @GopalAkshintala overfullstack.ga
    71

    View Slide

  72. V li at ng n it er
    !" Before
    void validate(Map failureMap,
    List nonDuplicateEggs) {!!#}
    !" After
    static Either, ImmutableEgg> validate(
    final Either, ImmutableEgg> egg) {!!#}
    @GopalAkshintala overfullstack.ga
    72

    View Slide

  73. V li at ng n it er
    static Either, ImmutableEgg> validate(
    Either, ImmutableEgg> eggToValidate) {
    return eggToValidate !" No validations execute if egg is Either.left
    .flatMap(validateAge)
    .flatMap(validateField1)
    !!#
    !!#
    ;
    }
    @GopalAkshintala overfullstack.ga
    73

    View Slide

  74. 74

    View Slide

  75. R fa to ed - N S ri gs tt ch d!
    !" void filterDuplicates(Map failureMap, List eggsFromRequest) {!!#}
    static List, ImmutableEgg!$ filterDuplicates(final List eggsFromRequest) {!!#}
    !" void validate(Map failureMap, List nonDuplicateEggs) {!!#}
    static Either, ImmutableEgg> validate(final Either, ImmutableEgg> egg) {!!#}
    !" List toEntityObjs(List validEggs) {!!#}
    static Either, EggEntity> toEntityObj(final Either, ImmutableEgg> egg) {!!#}
    !" Partition
    !" void bulkInsertIntoDB(List eggEntityObjs) throws DMLOperationException {!!#}
    static Either eggEntityObjs) {!!#}
    @GopalAkshintala overfullstack.ga
    75

    View Slide

  76. L ke M th er va io
    static List, ImmutableEgg!" filterDuplicates(final List eggsFromRequest) {!!#}
    static Either, ImmutableEgg> validate(final Either, ImmutableEgg> egg) {!!#}
    static Either, EggEntity> toEntityObj(final Either, ImmutableEgg> egg) {!!#}
    !$ Partition
    static Either eggEntityObjs) {!!#}
    @GopalAkshintala overfullstack.ga
    76

    View Slide

  77. L t’s la s me eg
    @GopalAkshintala overfullstack.ga
    77

    View Slide

  78. F nc io C mp si io
    final var partition = filterDuplicates(immutableEggsFromRequest).stream()
    .map(EggService!"validate)
    .map(EggService!"toEntityObj)
    .collect(Collectors.partitioningBy(Either!"isRight));
    final var objsToInsert =
    partition.get(true).stream().map(Either!"get).toList();
    final var results = bulkInsertIntoDB(objsToInsert);
    !# Prepare response from `partition.get(false)` and `results`
    @GopalAkshintala overfullstack.ga
    78

    View Slide

  79. H w re e oi g n ur
    oa ?
    L t's sk ur et ic
    @GopalAkshintala overfullstack.ga
    79

    View Slide

  80. M tr c - 1
    C gn ti e om le it
    @GopalAkshintala overfullstack.ga
    80

    View Slide

  81. C gn ti el C mp ex
    filterDuplicates(failureMap, eggsFromRequest);
    !!"
    !# Partition
    !!"
    validate(failureMap, nonDuplicateEggs);
    !!"
    !# Partition
    !!"
    var eggEntityObjs = toEntityObjs(validEggs);
    try {
    bulkInsertIntoDB(eggEntityObjs);
    } catch (DMLOperationException ex) {
    handlePartialFailures(failureMap, ex);
    }
    !# Prepare response from `failureMap` and db insertion results
    @GopalAkshintala overfullstack.ga
    81

    View Slide

  82. D d e ⊗ C gn ti e om le it ?
    final var partition = filterDuplicates(immutableEggsFromRequest).stream()
    .map(EggService!"validate)
    .map(EggService!"toEntityObj)
    .collect(Collectors.partitioningBy(Either!"isRight));
    final var objsToInsert =
    partition.get(true).stream().map(Either!"get).toList();
    final var results = bulkInsertIntoDB(objsToInsert);
    !# Prepare response from `partition.get(false)` and `results`
    @GopalAkshintala overfullstack.ga
    82

    View Slide

  83. @GopalAkshintala overfullstack.ga
    83

    View Slide

  84. C mp ex ty
    ‣ Different types
    ‣ Accidental Complexity
    ‣ Essential complexity
    ‣ Cognitive/Cyclomatic complexity
    ‣ Different layers
    ‣ Unfamiliarity vs Unreadability.
    ‣ Strict vs Non-extensible.
    @GopalAkshintala overfullstack.ga
    84

    View Slide

  85. C mp ex ty er
    !
    final var partition = filterDuplicates(immutableEggsFromRequest).stream()
    .map(EggService!"validate)
    .map(EggService!"toEntityObj)
    .collect(Collectors.partitioningBy(Either!"isRight));
    final var objsToInsert =
    partition.get(true).stream().map(Either!"get).toList();
    final var results = bulkInsertIntoDB(objsToInsert);
    !# Prepare response from `partition.get(false)` and `results`
    !
    Essential Complexity
    !
    Unfamiliarity
    !
    Strictness
    @GopalAkshintala overfullstack.ga
    85

    View Slide

  86. !
    S na Qu e™
    T O je ti el m as re og it ve om le it
    !
    My previous talks, on the usage of SonarQube™:
    !
    Java Version
    !
    Kotlin Version
    @GopalAkshintala overfullstack.ga
    86

    View Slide

  87. M tr c - 2
    T st bi it
    @GopalAkshintala overfullstack.ga
    87

    View Slide

  88. B os y ur es ab li y
    @GopalAkshintala overfullstack.ga
    88

    View Slide

  89. !" * EggEntity is a legacy class and cannot be instantiated with `new`
    void fillEntityObj(Egg egg, EggEntity eggEntity) {
    if (egg.field1() !# null) {
    eggEntity.put(Fields.field1, egg.field1());
    }
    if (egg.field2() !# null) {
    eggEntity.put(Fields.field2, egg.field2());
    }
    if (egg.field3() !# null) {
    eggEntity.put(Fields.field3, egg.field3());
    }
    }
    @GopalAkshintala overfullstack.ga
    89

    View Slide

  90. B it le es
    @Test
    void fillEntityObjTest() {
    !" * `EggEntity` Object can't be instantiated in a JUnit context.
    final var mockEggEntity = mock(EggEntity.class);
    var egg = new Egg("field1Value", null, "field3Value");
    fillEntityObj(egg, mockEggEntity);
    verify(mockEggEntity).put(Fields.field1,"field1Value");
    verify(mockEggEntity, never()).put(Fields.field2, null);
    verify(mockEggEntity).put(Fields.field3,"field3Value");
    }
    @GopalAkshintala overfullstack.ga
    90

    View Slide

  91. B os y ur es ab li y
    U it es
    !==
    T st nt rn ls
    @GopalAkshintala overfullstack.ga
    91

    View Slide

  92. B os y ur es ab li y
    U it es
    ===
    E2E es f r he ni
    @GopalAkshintala overfullstack.ga
    92

    View Slide

  93. B os y ur es ab li y
    S pa at S at c ro
    S gn l
    @GopalAkshintala overfullstack.ga
    93

    View Slide

  94. S pa at S at c ro S gn l
    ‣ Static: 1-1 field mapping between Egg to EggEntity
    ‣ Signal: For each field mapping, fill non-null Egg
    fields into EggEntity using put.
    @GopalAkshintala overfullstack.ga
    94

    View Slide

  95. S pa at S at c ro S gn l
    !" Static
    static final MapMap.of(
    Fields.field1, ImmutableEgg!$field1,
    Fields.field2, ImmutableEgg!$field2,
    Fields.field3, ImmutableEgg!$field3);
    !" Call with Static data and Function reference
    fillEntityObj(egg, fieldMapping, EggEntity!$put);
    !" Signal
    static void fillEntityObj(
    ImmutableEgg egg,
    MapBiConsumer filler) {
    fieldMapping.forEach((fieldId, valueGetter) !% {
    final var fieldValue = valueGetter.apply(egg);
    if (fieldValue !& null) {
    filler.accept(fieldId, fieldValue);
    }
    });
    }
    @GopalAkshintala overfullstack.ga
    95

    View Slide

  96. T st nl t at at er
    @Test
    void fillEntityObjTest() {
    final var egg = ImmutableEgg.of("field1Value", null, "field3Value");
    var actualPairs = new HashMap();
    fillEntityObj(egg, actualPairs!"put);
    var expectedPairs = Map.of(
    Fields.field1, "field1Value",
    Fields.field3, "field3Value");
    assertEquals(expectedPairs, actualPairs);
    }
    @GopalAkshintala overfullstack.ga
    96

    View Slide

  97. B os y ur es ab li y
    D
    @GopalAkshintala overfullstack.ga
    97

    View Slide

  98. T y ot o nj ct nt re bj ct
    @Autowired
    public EggService(
    !!",
    @Qualifier(EGG_REPO) EggRepo eggRepo,
    !!"
    )
    @GopalAkshintala overfullstack.ga
    98

    View Slide

  99. I je t nl t e un ti n
    @Autowired
    public EggService(
    !!",
    !# @Qualifier(EGG_REPO) !# EggRepo eggRepo,
    @Qualifier(EGG_INSERTER) Function dbInserter,
    !!"
    )
    @GopalAkshintala overfullstack.ga
    99

    View Slide

  100. P rt & A ap er /C ea A ch te tu e
    w th ut xt a nt rf ce
    @Autowired
    public EggService(
    !!",
    !# @Qualifier(EGG_REPO) !# EggRepo eggRepo,
    @Qualifier(EGG_INSERTER) Function dbInserter,
    !!"
    )
    !# Original Config
    @Bean(EGG_INSERTER)
    public Function insert(
    @Qualifier(EGG_REPO) EggRepo eggRepo) {
    return eggRepo!$insert;
    }
    !# Test Config
    @Bean(EGG_INSERTER)
    public Function insertNoOp() {
    return eggEntity !% new ID(1); !#stub
    }
    @GopalAkshintala overfullstack.ga
    100

    View Slide

  101. M tr c - 2 - T st bi it
    T st bi it F rs
    T st ov ra e ol ow
    @GopalAkshintala overfullstack.ga
    101

    View Slide

  102. M tr c - 2 - T st bi it
    T st
    !
    R fa to
    (L st ut ot he as )
    @GopalAkshintala overfullstack.ga
    102

    View Slide

  103. @GopalAkshintala overfullstack.ga
    103

    View Slide

  104. @GopalAkshintala overfullstack.ga
    104

    View Slide

  105. @GopalAkshintala overfullstack.ga
    105

    View Slide

  106. R fa to I cr me ta ly
    A p es ri ed y DD
    @GopalAkshintala overfullstack.ga
    106

    View Slide

  107. @GopalAkshintala overfullstack.ga
    107

    View Slide

  108. !
    b t.l /h2a-s id s

    b t.l /h2a-c de
    !
    B og os
    @GopalAkshintala overfullstack.ga
    108

    View Slide

  109. @GopalAkshintala overfullstack.ga
    109

    View Slide