Phase III¶
Eventify¶
- Edit Lottery.ksmile
stream-module LotteryStream at com.metastay.lotterystream
- $smile
- Edit LotteryStream.kstream , add lottery related events
event LotteryCreated(lotteryName: String, amount: Int)
event ParticipantAdded(lotteryName: String, participantName: String)
event LotteryRan(lotteryName: String, winner: String)
- $compile
- Edit LotteryStreamTestSuite.scala
test("test1", Tag("event")) {
val created = LotteryCreated("Loto", 2000);
LotteryStreamWriter.appendEvent(created);
val ramAdded = ParticipantAdded("Loto", "ram");
LotteryStreamWriter.appendEvent(ramAdded);
val johnAdded = ParticipantAdded("Loto", "john");
LotteryStreamWriter.appendEvent(johnAdded);
val ran = LotteryRan("Loto", "john");
LotteryStreamWriter.appendEvent(ran);
}
test("test2", Tag("read")) {
LotteryStreamReader.size.println
}
- See conf file, Database and how the events are getiing stored.
Events and Commands¶
- Add stream dependency to Domain module in lottery.ksmile.
domain-module LotteryDomain(LotteryMongo,LotteryStream) at com.metastay.lotterydomain
- add stream to aggregate,and raise events from commands in LotteryDomain.kdomain
aggregate[LotteryStream] Lottery {
domain-ref lottery:Lottery
command create {
input(lotteryName:String, amount:Int)
pre {
require lotterMustNotExist "Lottery must not exist" => !(lottery.lotteryMustExist(input.lotteryName))
}
event-raised(created:LotteryCreated)
}
command addParticipant {
input(lotteryName:String, participantName:String)
pre {
require lotteryMustExists "Lottery must exist" => lottery.lotteryMustExist(input.lotteryName)
require newParticpant => !lottery.participantExist(input.lotteryName, input.participantName) failing "participant ${input.participantName} is already add to this lottery"
}
event-raised(added:ParticipantAdded)
}
command run {
input (lotteryName: String)
pre{
require notYetRun => lottery.lotteryOpen(input.lotteryName) failing "Lottery has already run!"
require existingLotteryName => lottery.lotteryNameExists(input.lotteryName) failing "Lottery name does not exist"
require atLeastTwoParticipants => existingLotteryName -> `domainRef.lottery.participantCount(input.lotteryName) > 1` failing "The lottery should have at least two participants"
}
event-raised(ran: LotteryRun)
output(winner: String)
- $smile
- $compile
- fix the compilation error
override def create = CreateCommand {
import CreateCommand._
input: Input =>
Event(LotteryCreated(input.lotteryName, input.amount)) -> Output()
}
override def addParticipant = AddParticipantCommand {
import AddParticipantCommand._
input: Input =>
Event(ParticipantAdded(input.lotteryName, input.participantName)) -> Output()
}
override def run = RunCommand {
import RunCommand._
input: Input =>
val participantCount = domainRef.lottery.participantCount(input.lotteryName)
val winnerIndex = scala.util.Random.nextInt(participantCount)
val participantList = LotteryQuery().lotteryName.is(input.lotteryName).findOne.get.participantList
val winner = participantList(winnerIndex)
Event(LotteryRan(input.lotteryName, winner)) -> Output(winner)
}
- Edit LotteryDomain.kdomain and add the handler after domain Lottery{ }
handler[LotteryStream] LotteryStream All
- $compile
- move the code from LotteryAggregateCode to LotteryStreamAllHandlerCode
override def handle(event: LotteryCreated, context: EventContext): Unit =
LotteryWriter.save(LotteryRow(lotteryName = event.lotteryName, amount = event.amount, open = true));
override def handle(event: ParticipantAdded, context: EventContext): Unit = {
val q = LotteryQuery().lotteryName.is(event.lotteryName);
val u = LotteryUpdate().participantList.addToSet(event.participantName);
LotteryWriter().updateOne(q, u)
}
override def handle(event: LotteryRan, context: EventContext): Unit = {
val q = LotteryQuery().lotteryName.is(event.lotteryName);
val u = LotteryUpdate().open.set(false).winner.set(event.winner);
LotteryWriter().updateOne(q, u)
}
- $run
- Test through url in browser http://localhost:9000/api/lottery/lottery-list
- or run any tests, nothing should change
More Commands!¶
- Edit CreateCommandTestSuite.scala
test("test1", Tag("p1")) {
LotteryWriter.drop();
grab[CreateCommandFixture]
.given()
.when(Input("WBLoto", 200))
.expectEvents(Event(LotteryCreated("WBLoto", 200)))
}
test("test2", Tag("n1")) {
LotteryWriter.drop();
grab[CreateCommandFixture]
.given(LotteryCreated("WBLoto", 100))
.when(Input("WBLoto", 200))
.expectRequireFail("lotterMustNotExist");
}
- $test-only com.metastay.lotterydomain.aggregate.lottery.CreateCommandTestSuite
- Edit AddParticipantCommandTestSuite.scala
test("test1", Tag("p1")) {
grab[AddParticipantCommandFixture]
.given(LotteryCreated("TestLottery", 1000))
.when(Input(lotteryName = "TestLottery",participantName = "XYZ"))
.expectEvents(Event(ParticipantAdded(lotteryName = "TestLottery",participantName = "XYZ")))
}
test("test2", Tag("n1")) {
grab[AddParticipantCommandFixture]
.given(LotteryCreated("TestLottery", 1000), ParticipantAdded(lotteryName = "TestLottery",participantName = "XYZ") )
.when(Input(lotteryName = "TestLottery",participantName = "XYZ"))
.expectRequireFail
}
- $test-only com.metastay.lotterydomain.aggregate.lottery.AddParticipantCommandTestSuite
Projection¶
- Edit Lottery.ksmile - Add dependency of LotteryQuery in the Play module as well.
mongo-module LotteryReadMongo at com.metastay.lotteryreadmongo
query-module LotteryQuery(LotteryReadMongo, LotteryStream) at com.metastay.lotteryquery
- $smile,refresh eclipse
- edit LotteryReadMongo.kmongo
collection LotteryRead {
property lotteryName:String
property amount:Int
property participantList:String*
property winner:String?
property open:Boolean
}
- edit LotterQuery.kqyuery
projector [LotteryStream] Lottery All
- $compile
- Edit LotteryAllProjectorCode
override def project(event: LotteryCreated, context: EventContext): Unit = {
LotteryReadWriter().save(LotteryReadRow(lotteryName = event.lotteryName, amount = event.amount, open = true))
}
override def project(event: ParticipantAdded, context: EventContext): Unit = {
val q = LotteryReadQuery().lotteryName.is(event.lotteryName);
val u = LotteryReadUpdate().participantList.addToSet(event.participantName)
LotteryReadWriter().updateOne(q, u)
}
override def project(event: LotteryRan, context: EventContext): Unit = {
val q = LotteryReadQuery().lotteryName.is(event.lotteryName)
val u = LotteryReadUpdate().open.set(false).winner.set(event.winner)
LotteryReadWriter().updateOne(q, u)
}
- Edit LotteryWebReaderCode
override def lotteryList: Request[LotteryListView.Input] => List[Lottery] = {
import LotteryListView._
request: Request[Input] =>
val input = request.body;
LotteryReadQuery().find.map(
row =>
Lottery(
lotteryName = row.lotteryName,
amount = row.amount,
participantList = row.participantList,
status = if(row.open) "OPEN" else "CLOSED",
winner = row.winner
)
)
}
- clean the db
- $run
- Test through url in browser http://localhost:9000/api/lottery/lottery-list
Review Write DB¶
- we dont really need amount and winner in write db, they dont get involve in any of the decision making in domain.
- edit LotteryMongo.kmongo in eclipse
collection Lottery {
property lotteryName:String
//property amount:Int
property participantList:String*
//property winner:String?
property open:Boolean
}
- $compile, fix the error
- Test through curl
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”DLoto”,”amount”:1000}’ http://localhost:9000/api/lottery/create-lottery
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”DLoto”,”participantName”:”John”}’ http://localhost:9000/api/lottery/ add-participant
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”DLoto”,”participantName”:”Jim”}’ http://localhost:9000/api/lottery/add-participant
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”DLoto”,”participantName”:”Joe”}’ http://localhost:9000/api/lottery/add-participant
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”DLoto”}’ http://localhost:9000/api/lottery/run
Send Email¶
- Sending email to winner needs to capture email addresses for the participants, needs to change database to capture the email address
- Capturing email of a participant and sending email to the winner
- Edit LotteryReadMongo.kmongo
collection LotteryRead {
property lotteryName:String
property amount:Int
reference participantList:Participant*
property winner:String?
property open:Boolean
}
collection Participant {
property name:String
property email:String
}
- participant’s email needs to be added to the event
- Edit LotteryStream.kstream
event ParticipantAdded(lotteryName: String, participantName: String,email:String)
- Edit LotteryDomain.kdomain,replace addParticipant command with the following,
command addParticipant {
input(lotteryName:String, participantName:String,participantEmail: String)
pre {
require lotteryMustExists "Lottery must exist" => lottery.lotteryMustExist(input.lotteryName)
require newParticpant => !lottery.participantExist(input.lotteryName, input.participantName) failing "participant ${input.participantName} is already add to this lottery"
}
event(added:ParticipantAdded)
}
- $compile, and fix the compiler errors
- Edit LotteryAllProjectorCode
override def project(event: ParticipantAdded, context: EventContext): Unit = {
val q = LotteryReadQuery().lotteryName.is(event.lotteryName);
Participant p = ParticipantWriter.save(ParticipantRow(event.participantName,event.participantEmail))
val u = LotteryReadUpdate().participantList.addToSet(p)
LotteryReadWriter.updateOne(q, u)
}
- $compile, fix the error
- Test through curl
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”DLoto”,”amount”:1000}’ http://localhost:9000/api/lottery/create-lottery
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”DLoto”,”participantName”:”John”}’ http://localhost:9000/api/lottery/ add-participant
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”DLoto”,”participantName”:”Jim”}’ http://localhost:9000/api/lottery/add-participant
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”DLoto”,”participantName”:”Joe”}’ http://localhost:9000/api/lottery/add-participant
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”DLoto”}’ http://localhost:9000/api/lottery/run