Share
Embed
Transcript
59 slides
-
Why? How am I supposed to work even harder? !Most
examples are over-simplified! -
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 …. -
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 …. -
Inheritance User Rating Movie Actor name dob …. title stars
…. title plot …. name dob …. Entity uuid createdAt / updatedAt / etc. -
Inheritance User Rating Movie Actor name dob …. title stars
…. title plot …. name dob …. Entity uuid createdAt / updatedAt / etc. -
Inheritance User Rating Movie Actor name dob …. title stars
…. title plot …. …. Entity …. Person uuid -
Inheritance User Rating Movie Actor name dob …. title stars
…. title plot …. …. Entity …. Person uuid -
Lombok Actor @ToString class Movie Entity uuid createdAt title actors
Movie Movie(title=The Matrix, actors=[Actor(internationalActive=true)]) -
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,...)]) -
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 -
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 -
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} ] } -
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"} ] } ] } -
Data loading (e.g. JPA) User data Rating data and Movie
data OneToMany Ratings -
Data loading (e.g. JPA) User data Rating data Movie data
OneToMany(fetch = FetchType.EAGER) Ratings -
Data loading (e.g. JPA) User data Rating data Movie data
OneToMany Ratings @Entity @NamedEntityGraph(name = "User.ratings", attributeNodes = @NamedAttributeNode("ratings") ) public class User { //... } -
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); } -
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); } -
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 -
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()); } -
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()); } -
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()); } } -
Writing readable code: Avoid Utils-classes public class Movie { public
static Movie createDefault() { return new Movie( UUID.randomUUID().toString(), "<no title>", LocalDateTime.now()); } } -
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()); } } -
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 } } -
public void update(Movie movie) throws Exception { if (!movie.hasMetadata()) {
updateMetadata(); } if (!movie.hasImage()) { updateImage(); } } Writing readable code: Nesting / Extraction -
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; } -
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(); } -
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 -
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 -
Control and communicate your architecture • ArchUnit • API Guardian
@API(status = API.Status.STABLE/INTERNAL) • jQAssistant -
Yes, you can use Optional, If you are not doing
real time big data processing