Skip to content

Instantly share code, notes, and snippets.

@beyondxscratch
beyondxscratch / pom.xml
Last active August 9, 2020 19:47
Maven Enforcer Plugin to seal a domain in Hexagonal Architecture
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
@beyondxscratch
beyondxscratch / CreateProfile.kt
Last active August 9, 2020 23:00
CreateProfile.kt
package org.craftsrecords.talkadvisor.recommendation.api
import org.craftsrecords.talkadvisor.recommendation.preferences.Preferences
import org.craftsrecords.talkadvisor.recommendation.profile.Profile
@FunctionalInterface
interface CreateProfile {
fun forUserWithPreferences(userId: String, preferences: Preferences): Profile
}
@Aggregate
class Recommendation(val id: UUID = UUID.randomUUID(), val criteria: Criteria, talks: Set<Talk>) {
val talks: Set<Talk>
init {
checkCriteriaTalkFormatsCorrespondToTheTalksOne(talks)
this.talks = talks.toSet()
}
@DomainService
class TalksAdvisor(private val searchTalks: SearchTalks,
private val recommendations: Recommendations,
private val profiles: Profiles) : RecommendTalks {
override fun to(userId: String): Recommendation {
val profile = profiles.fetch(userId) ?: throw ProfileNotFoundException(userId)
return recommendTalksSatisfying(profile.preferences)
}
package org.craftsrecords.talkadvisor.recommendation.spi
import org.craftsrecords.hexarch.Repository
import org.craftsrecords.talkadvisor.recommendation.profile.Profile
@Repository
interface Profiles {
fun save(profile: Profile): Profile
fun fetch(userId: String): Profile?
Feature: As a frequent user,
In order not repeat my preferences at each request,
I want to create my profile with my preferences
Scenario: The user is creating his profile with his preferences
Given a user
And he wants to learn
| DDD | hexagonal architecture |
And he only wants to see
package org.craftsrecords.talkadvisor.recommendation.spi.stubs
import org.craftsrecords.hexarch.Stub
import org.craftsrecords.talkadvisor.recommendation.preferences.Topic
import org.craftsrecords.talkadvisor.recommendation.spi.SearchTalks
import org.craftsrecords.talkadvisor.recommendation.talk.Talk
import org.craftsrecords.talkadvisor.recommendation.talk.TalkFormat
import java.util.*
@Stub
class Talk private constructor(id: String,
title: String,
duration: Duration) {
val id = notBlank(id, "Cannot create a Talk is a blank id")!!
val title = notBlank(title, "Cannot create a Talk is a blank title")!!
val duration = notNegative(duration)
val format = TalkFormat.ofDuration(duration)
}
@Test
fun `should retrieve talks`() {
val mockedTalk = mock(Talk::class.java)
given(mockedTalk.format).willReturn(IGNITE)
given(mockedTalk.duration).willReturn(Duration.ofHours(1))
// this resulted Talk Object is invalid, the source of truth is the duration
// but here with mockito we overload the format computation logic
given(searchTalks.forTopics(anySet())).willReturn(setOf(mockedTalk))
package org.craftsrecords.talkadvisor.recommendation.stepdefs
import cucumber.api.java.en.Then
import cucumber.api.java.en.When
import org.craftsrecords.talkadvisor.recommendation.api.RecommendTalks
import org.craftsrecords.talkadvisor.recommendation.assertions.that
import org.craftsrecords.talkadvisor.recommendation.criteria.GuestCriteria
import org.craftsrecords.talkadvisor.recommendation.talk.TalkFormat
import java.time.Duration.ofMinutes