Skip to content

Commit 9c6a54f

Browse files
committed
Add backoffice courses repository cache
1 parent ea63495 commit 9c6a54f

File tree

8 files changed

+180
-2
lines changed

8 files changed

+180
-2
lines changed

apps/test/tv/codely/apps/ApplicationTestCase.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ protected void assertResponse(
3434
String expectedResponse
3535
) throws Exception {
3636
ResultMatcher response = expectedResponse.isEmpty()
37-
? content().string("")
38-
: content().json(expectedResponse);
37+
? content().string("")
38+
: content().json(expectedResponse);
3939

4040
mockMvc
4141
.perform(get(endpoint))
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package tv.codely.backoffice.courses.infrastructure.persistence;
2+
3+
import tv.codely.backoffice.courses.domain.BackofficeCourse;
4+
import tv.codely.backoffice.courses.domain.BackofficeCourseRepository;
5+
import tv.codely.shared.domain.criteria.Criteria;
6+
7+
import java.util.ArrayList;
8+
import java.util.HashMap;
9+
import java.util.List;
10+
11+
public final class InMemoryCacheBackofficeCourseRepository implements BackofficeCourseRepository {
12+
private final BackofficeCourseRepository repository;
13+
private List<BackofficeCourse> courses = new ArrayList<>();
14+
private HashMap<String, List<BackofficeCourse>> matchingCourses = new HashMap<>();
15+
16+
public InMemoryCacheBackofficeCourseRepository(BackofficeCourseRepository repository) {
17+
this.repository = repository;
18+
}
19+
20+
@Override
21+
public void save(BackofficeCourse course) {
22+
repository.save(course);
23+
}
24+
25+
@Override
26+
public List<BackofficeCourse> searchAll() {
27+
return courses.isEmpty() ? searchAndFillCache() : courses;
28+
}
29+
30+
@Override
31+
public List<BackofficeCourse> matching(Criteria criteria) {
32+
return matchingCourses.containsKey(criteria.serialize())
33+
? matchingCourses.get(criteria.serialize())
34+
: searchMatchingAndFillCache(criteria);
35+
}
36+
37+
private List<BackofficeCourse> searchMatchingAndFillCache(Criteria criteria) {
38+
List<BackofficeCourse> courses = repository.matching(criteria);
39+
40+
this.matchingCourses.put(criteria.serialize(), courses);
41+
42+
return courses;
43+
}
44+
45+
private List<BackofficeCourse> searchAndFillCache() {
46+
List<BackofficeCourse> courses = repository.searchAll();
47+
48+
this.courses = courses;
49+
50+
return courses;
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package tv.codely.backoffice.courses.infrastructure.persistence;
2+
3+
import org.junit.jupiter.api.BeforeEach;
4+
import org.junit.jupiter.api.Test;
5+
import org.mockito.Mockito;
6+
import tv.codely.backoffice.BackofficeContextInfrastructureTestCase;
7+
import tv.codely.backoffice.courses.domain.BackofficeCourse;
8+
import tv.codely.backoffice.courses.domain.BackofficeCourseCriteriaMother;
9+
import tv.codely.backoffice.courses.domain.BackofficeCourseMother;
10+
import tv.codely.backoffice.courses.domain.BackofficeCourseRepository;
11+
import tv.codely.shared.domain.criteria.Criteria;
12+
13+
import java.util.Arrays;
14+
import java.util.List;
15+
16+
import static org.hamcrest.MatcherAssert.assertThat;
17+
import static org.hamcrest.Matchers.containsInAnyOrder;
18+
import static org.mockito.Mockito.*;
19+
20+
final class InMemoryCacheBackofficeCourseRepositoryShould extends BackofficeContextInfrastructureTestCase {
21+
private InMemoryCacheBackofficeCourseRepository repository;
22+
private BackofficeCourseRepository innerRepository;
23+
24+
@BeforeEach
25+
protected void setUp() {
26+
innerRepository = mock(BackofficeCourseRepository.class);
27+
repository = new InMemoryCacheBackofficeCourseRepository(innerRepository);
28+
}
29+
30+
@Test
31+
void save_a_course() {
32+
BackofficeCourse course = BackofficeCourseMother.random();
33+
34+
repository.save(course);
35+
36+
shouldHaveSaved(course);
37+
}
38+
39+
@Test
40+
void search_all_existing_courses() {
41+
BackofficeCourse course = BackofficeCourseMother.random();
42+
BackofficeCourse anotherCourse = BackofficeCourseMother.random();
43+
List<BackofficeCourse> courses = Arrays.asList(course, anotherCourse);
44+
45+
shouldSearchAll(courses);
46+
47+
assertThat(courses, containsInAnyOrder(repository.searchAll().toArray()));
48+
}
49+
50+
@Test
51+
void search_all_existing_courses_without_hitting_the_inner_repository_the_second_time() {
52+
BackofficeCourse course = BackofficeCourseMother.random();
53+
BackofficeCourse anotherCourse = BackofficeCourseMother.random();
54+
List<BackofficeCourse> courses = Arrays.asList(course, anotherCourse);
55+
56+
shouldSearchAll(courses);
57+
58+
assertThat(courses, containsInAnyOrder(repository.searchAll().toArray()));
59+
assertThat(courses, containsInAnyOrder(repository.searchAll().toArray()));
60+
}
61+
62+
@Test
63+
void search_courses_using_a_criteria() {
64+
BackofficeCourse matchingCourse = BackofficeCourseMother.create("DDD en Java", "3 days");
65+
BackofficeCourse anotherMatchingCourse = BackofficeCourseMother.create("DDD en TypeScript", "2.5 days");
66+
List<BackofficeCourse> matchingCourses = Arrays.asList(matchingCourse, anotherMatchingCourse);
67+
68+
Criteria criteria = BackofficeCourseCriteriaMother.nameAndDurationContains("DDD", "days");
69+
70+
shouldSearchMatching(criteria, matchingCourses);
71+
72+
assertThat(matchingCourses, containsInAnyOrder(repository.matching(criteria).toArray()));
73+
}
74+
75+
@Test
76+
void search_courses_using_a_criteria_without_hitting_the_inner_repository_the_second_time() {
77+
BackofficeCourse matchingCourse = BackofficeCourseMother.create("DDD en Java", "3 days");
78+
BackofficeCourse anotherMatchingCourse = BackofficeCourseMother.create("DDD en TypeScript", "2.5 days");
79+
List<BackofficeCourse> matchingCourses = Arrays.asList(matchingCourse, anotherMatchingCourse);
80+
81+
Criteria criteria = BackofficeCourseCriteriaMother.nameAndDurationContains("DDD", "days");
82+
83+
shouldSearchMatching(criteria, matchingCourses);
84+
85+
assertThat(matchingCourses, containsInAnyOrder(repository.matching(criteria).toArray()));
86+
assertThat(matchingCourses, containsInAnyOrder(repository.matching(criteria).toArray()));
87+
}
88+
89+
private void shouldSearchAll(List<BackofficeCourse> courses) {
90+
Mockito.when(innerRepository.searchAll()).thenReturn(courses);
91+
}
92+
93+
private void shouldSearchMatching(Criteria criteria, List<BackofficeCourse> courses) {
94+
Mockito.when(innerRepository.matching(criteria)).thenReturn(courses);
95+
}
96+
97+
private void shouldHaveSaved(BackofficeCourse course) {
98+
verify(innerRepository, atLeastOnce()).save(course);
99+
}
100+
}

src/shared/main/tv/codely/shared/domain/criteria/Criteria.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,14 @@ public Optional<Integer> offset() {
4141
public boolean hasFilters() {
4242
return filters.filters().size() > 0;
4343
}
44+
45+
public String serialize() {
46+
return String.format(
47+
"%s~~%s~~%s~~%s",
48+
filters.serialize(),
49+
order.serialize(),
50+
offset.orElse(0),
51+
limit.orElse(0)
52+
);
53+
}
4454
}

src/shared/main/tv/codely/shared/domain/criteria/Filter.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,8 @@ public FilterOperator operator() {
4040
public FilterValue value() {
4141
return value;
4242
}
43+
44+
public String serialize() {
45+
return String.format("%s.%s.%s", field.value(), operator.value(), value.value());
46+
}
4347
}

src/shared/main/tv/codely/shared/domain/criteria/FilterOperator.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,8 @@ public static FilterOperator fromValue(String value) {
2929
public boolean isPositive() {
3030
return this != NOT_EQUAL && this != NOT_CONTAINS;
3131
}
32+
33+
public String value() {
34+
return operator;
35+
}
3236
}

src/shared/main/tv/codely/shared/domain/criteria/Filters.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,8 @@ public static Filters none() {
2323
public List<Filter> filters() {
2424
return filters;
2525
}
26+
27+
public String serialize() {
28+
return filters.stream().map(Filter::serialize).collect(Collectors.joining("^"));
29+
}
2630
}

src/shared/main/tv/codely/shared/domain/criteria/Order.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,8 @@ public OrderType orderType() {
3939
public boolean hasOrder() {
4040
return !orderType.isNone();
4141
}
42+
43+
public String serialize() {
44+
return String.format("%s.%s", orderBy.value(), orderType.value());
45+
}
4246
}

0 commit comments

Comments
 (0)