Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Work harder not smarter

Gerrit Meier
September 13, 2023

Work harder not smarter

Talk given at JavaForumNord 2023 first.

Gerrit Meier

September 13, 2023
Tweet

More Decks by Gerrit Meier

Other Decks in Programming

Transcript

  1. Why? How am I supposed to work even harder? !Most

    examples are over-simplified!
  2. User Rating Movie Actor uuid createdAt / updatedAt / etc.

    name dob …. uuid createdAt / updatedAt / etc. title stars …. uuid createdAt / updatedAt / etc. title plot …. uuid createdAt / updatedAt / etc. name dob ….
  3. User Rating Movie Actor uuid createdAt / updatedAt / etc.

    name dob …. uuid createdAt / updatedAt / etc. title stars …. uuid createdAt / updatedAt / etc. title plot …. uuid createdAt / updatedAt / etc. name dob ….
  4. Inheritance User Rating Movie Actor name dob …. title stars

    …. title plot …. name dob …. Entity uuid createdAt / updatedAt / etc.
  5. Inheritance User Rating Movie Actor name dob …. title stars

    …. title plot …. name dob …. Entity uuid createdAt / updatedAt / etc.
  6. Inheritance User Rating Movie Actor name dob …. title stars

    …. title plot …. …. Entity …. Person uuid
  7. Inheritance User Rating Movie Actor name dob …. title stars

    …. title plot …. …. Entity …. Person uuid
  8. Lombok Actor @ToString class Movie Entity uuid createdAt title actors

    Movie Movie(title=The Matrix, actors=[Actor(internationalActive=true)])
  9. Lombok Actor @ToString(callSuper = true) class Movie Entity uuid createdAt

    title actors Movie Movie(super= Entity(uuid=c1e3c57e-...-b73147a13bfa, createdAt=2023-09-05T10:26:31.305), title=The Matrix, actors=[Actor(dob=1982-03-14,name=Your next Superstar,internationalActive=true,...)])
  10. Lombok Actor @ToString(callSuper = true) class Movie Entity uuid createdAt

    title actors Movie Exception in thread "main" java.lang.StackOverflowError at java.lang.AbstractStringBuilder.append(AbstractStringBuilde r.java:449) at java.lang.StringBuilder.append(StringBuilder.java:141) at com.example.domain.Entity.toString(Entity.java:11) at com.example.domain.Movie.toString(Movie.java:11) at java.lang.String.valueOf(String.java:2994) at java.lang.StringBuilder.append(StringBuilder.java:136) at java.util.AbstractCollection.toString(AbstractCollection.ja va:462) at java.lang.String.valueOf(String.java:2994) at
  11. Jackson Actor new ObjectMapper() .writeValueAsString(movie) Entity uuid createdAt title actors

    Movie Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[0]->com.example.domain.Movie["actors"]- >java.util.ArrayList[0]->com.example.domain.Actor["movies"] ->java.util.ArrayList[0]->com.example.domain.Movie["actors" ]->java.util.ArrayList[0]->com.example.domain.Actor["movies "]->java.util.ArrayList[0]->com.example.domain.Movie["actor s"]->java.util.ArrayList[0]->com.example.domain.Actor["movi es"]->java.util.ArrayList[0]->com.example.domain.Movie["act ors"]->java.util.ArrayList[0]->com.example.domain.Actor["mo vies"]->java.util.ArrayList[0]->com.example.domain.Movie["a ctors"]->java.util.ArrayList[0]->com.example.domain.Actor[" movies"]->java.util.ArrayList[0]->com.example.domain.Movie[ "actors"]->java.util.ArrayList[0]->com.example.domain.Actor
  12. Jackson Actor class Actor { @JsonIgnore public List<Movie> movies; }

    Entity uuid createdAt title actors Movie {"uuid":"xxxx", "createdAt":[2023,9,5,11,0,58,264000000], "title":"The Matrix", "actors":[{"uuid":"yyyy", "createdAt":[2023,9,5,11,0,58,265000000], "name":"Your next Superstar", "dob":"1982-03-14", "internationalActive":true} ] }
  13. Jackson Actor class Actor { @JsonIgnoreProperties("actors") public List<Movie> movies; }

    Entity uuid createdAt title actors Movie {"uuid":"xxxx", "createdAt":[2023,9,5,11,2,8,999000000], "title":"The Matrix", "actors":[{"uuid":"yyyy", "createdAt":[2023,9,5,11,2,9,1000000], "name":"Your next Superstar", "dob":"1982-03-14", "internationalActive":true, "movies":[{"uuid":"xxxx", "createdAt":[2023,9,5,11,2,8,999000000], "title":"The Matrix"} ] } ] }
  14. Data loading (e.g. JPA) User data Rating data Movie data

    OneToMany(fetch = FetchType.EAGER) Ratings
  15. Data loading (e.g. JPA) User data Rating data Movie data

    OneToMany Ratings @Entity @NamedEntityGraph(name = "User.ratings", attributeNodes = @NamedAttributeNode("ratings") ) public class User { //... }
  16. Data loading (e.g. JPA) User data Rating data Movie data

    OneToMany Ratings @Entity @NamedEntityGraph(name = "User.ratings", attributeNodes = @NamedAttributeNode("ratings") ) public class User { //... } @Repository public interface UserRepository extends JpaRepository<User, String> { @EntityGraph(value = "User.ratings") User findEagerById(String id); }
  17. Data loading (e.g. JPA) User data Rating data Movie data

    OneToMany Ratings @Repository public interface UserRepository extends JpaRepository<User, String> { @EntityGraph(attributePaths = {"ratings"}) User findEagerById(String id); }
  18. Data loading • Model your domain to cover all data

    needed • Read and understand the specifications of your persistence mapper • Use built-in mechanisms to load the data for your use-case • Don’t try to work against the mapper library ◦ Use lower abstractions or drivers
  19. Writing readable code public List<String> getValue(Movie m) { String x

    = "|"; return m.getRelated().stream() .map(a -> { String v1 = a.getValue1(); String v2 = a.getValue2(); return v1 + x + v2; }) .collect(Collectors.toList()); }
  20. Writing readable code public List<String> getValue(Movie movie) { String fieldSeparator

    = "|"; return movie.getActors().stream() .map(actor -> { String actorName = actor.getName(); String actorDob = actor.getDob(); return actorName + fieldSeparator + actorDob; }) .collect(Collectors.toList()); }
  21. Writing readable code: Avoid Utils-classes public class DomainUtils { public

    static Movie createMovieWithDefaults() { return new Movie( UUID.randomUUID().toString(), "<no title>", LocalDateTime.now()); } public static List<Movie> top5Movies(List<Movie> movies) { return movies.stream() .sorted((movie1, movie2) -> movie2.getAverageRating().compareTo(movie1.getAverageRating())) .limit(5) .collect(Collectors.toList()); } }
  22. Writing readable code: Avoid Utils-classes public class Movie { public

    static Movie createDefault() { return new Movie( UUID.randomUUID().toString(), "<no title>", LocalDateTime.now()); } }
  23. Writing readable code: Avoid Utils-classes public class Movies { public

    static List<Movie> top5Movies(List<Movie> movies) { return movies.stream() .sorted((movie1, movie2) -> movie2.getAverageRating().compareTo(movie1.getAverageRating())) .limit(5) .collect(Collectors.toList()); } }
  24. Writing readable code: Nesting / Extraction public void update(Movie movie)

    throws Exception { if (!movie.hasMetadata()) { HttpResponse httpResponse = HttpClientBuilder.create().build().execute(new HttpGet("METADATA_URL")); switch (httpResponse.getStatusLine().getStatusCode()) { case 200: BufferedReader responseReader = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent())); String response = responseReader.lines().collect(Collectors.joining()); // movie.set... // default } } if (!movie.hasImage()) { HttpResponse httpResponse = HttpClientBuilder.create().build().execute(new HttpGet("IMAGE_URL")); switch (httpResponse.getStatusLine().getStatusCode()) { case 200: BufferedReader responseReader = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent())); String response = responseReader.lines().collect(Collectors.joining()); // movie.setImage(...) // default } }
  25. public void update(Movie movie) throws Exception { if (!movie.hasMetadata()) {

    updateMetadata(); } if (!movie.hasImage()) { updateImage(); } } Writing readable code: Nesting / Extraction
  26. Writing readable code: Inversion public String getOldestActor(Movie movie) { String

    oldestActorName; List<Actor> actors = movie.getActors(); if (actors != null) { Actor oldestActor = actors.stream() .sorted(Comparator.comparing(Person::getDob)) .findFirst().get(); oldestActorName = oldestActor.getName(); } else { oldestActorName = ""; } return oldestActorName; }
  27. Writing readable code: Inversion public String getOldestActor(Movie movie) { List<Actor>

    actors = movie.getActors(); if (actors == null) { return ""; } Actor oldestActor = actors.stream() .sorted(Comparator.comparing(Person::getDob)) .findFirst().get(); return oldestActor.getName(); }
  28. Have friendly exception messages • Answer some questions ◦ How

    did “we” get here? ◦ How can “we” mitigate the situation? ▪ Documentation/GH-Issues • Be empathetic • You can still be technical ◦ What information is needed to support the customer • Don’t be too verbose • NEVER throw “expected” exceptions
  29. Create a good documentation • Have a dedicated technical writer

    ◦ Implicit testing of your application and the documentation state • Give the documentation to new team members ◦ Helps with onboarding and also shows lacks in the documentation • (if possible) share link to the open source repository ◦ Problems/misunderstandings can be filed as issues ◦ Customers and users can create PRs
  30. Control and communicate your architecture • ArchUnit • API Guardian

    @API(status = API.Status.STABLE/INTERNAL) • jQAssistant
  31. Yes, you can use Optional, If you are not doing

    real time big data processing