Phase II¶
Lottery Domain Module¶
‘Create Lottery’ Command¶
- Add a domain module in Lottery.ksmile
Lottery.ksmile
domain-module LotteryDomain at com.metastay.lotterydomain
- $smile
- Refresh Eclipse to see LotteryDomain.kdomain
- Add Lottery.create Command
LotteryDomain.kdomain
domain-logic Lottery {
function lotteryNameExists(lotteryName:String):Boolean
}
command-set Lottery {
domain-logic-ref lottery:Lottery
command create {
input(lotteryName:String, amount:Int)
pre {
condition NewLotteryName => !(lottery.lotteryNameExists(input.lotteryName)) failing "Lottery name already exists!"
}
}
}
- $compile
- Open LotteryCommandSetCode to save it to implement.
Warning
LotteryWriter and other mongo related files not available to implement because LotteryDomain is not dependent on LotteryMongo.
- Add the LotteryMongo dependency in LotteryDomain in Lottery.ksmile file
Lottery.ksmile
section Lottery {
mongo-module LotteryMongo at com.metastay.lotterymongo
domain-module LotteryDomain(LotteryMongo) at com.metastay.lotterydomain
play-module LotteryPlay(LotteryMongo) at lotteryplay
}
- $smile
- Open LotteryCommandSetCode to implement create command as below.
LotteryCommandSetCode.scala
override def create = CreateCommand {
import CreateCommand._;
input: Input =>
LotteryWriter().save(LotteryRow(lotteryName = input.lotteryName, amount = input.amount, open = false))
}
- Open LotteryDomainLogicCode to implement lotteryNameExists method as below
LotteryDomainLogicCode
override def lotteryNameExists (lotteryName:String):Boolean = LotteryQuery().lotteryName.is(lotteryName).exists
- $compile
- Write test in LotteryDomainTestSuite as below.
LotteryDomainTestSuite
test("create", Tag("create")) {
val lotteryCommandSet= grab[LotteryCommandSet]
lotteryCommandSet.create("WBLoto", 20000).println;
}
- $project LotteryDomain
- $test-only com.metastay.lotterydomain.LotteryDomainTestSuite – -n create
- Check in DB if the WBLoto Lottery has been created
‘Add Participant’ And ‘Run’Command¶
Adding two more commands.
LotteryDomain.kdomain
domain-logic Lottery {
function lotteryNameExists(lotteryName:String):Boolean
function participantExist(lotteryName:String, participantName:String):Boolean
function participantCount(lotteryName:String):Int
}
command-set Lottery {
domain-logic-ref lottery:Lottery
command create {
input(lotteryName:String, amount:Int)
pre {
condition NewLotteryName => !(lottery.lotteryNameExists(input.lotteryName)) failing "Lottery name already exists!"
}
}
command addParticipant {
input(lotteryName:String, participantName:String)
pre {
condition lotteryMustExists "Lottery must exist" => lottery.lotteryNameExists(input.lotteryName)
condition particpantNotYetAdded "Participant Not yet added" => !lottery.participantExist(input.lotteryName, input.participantName)
}
}
command run {
input(lotteryName:String)
pre {
condition ExistingLotteryName => lottery.lotteryNameExists(input.lotteryName) failing "Lottery name does not exists"
condition atLeastTwoParticipants => ExistingLotteryName -> `domainRef.lottery.participantCount(input.lotteryName) > 1` failing "The lottery does not have any participant!"
}
output(winner:String)
}
}
- $compile
- Implementing addParticipant() and run() in LotteryCommandSetCode
LotteryCommandSetCode.scala
class LotteryCommandSetCode(domainRef: DomainRef) extends LotteryCommandSetCommands {
override def create = CreateCommand {
import CreateCommand._;
input: Input =>
LotteryWriter().save(LotteryRow(lotteryName = input.lotteryName, amount = input.amount))
}
override def addParticipant = AddParticipantCommand {
import AddParticipantCommand._;
input: Input =>
val q = LotteryQuery().lotteryName.is(input.lotteryName)
val u = LotteryUpdate().participantList.addToSet(input.participantName)
LotteryWriter().updateOne(q, u)
}
override def run = RunCommand {
import RunCommand._;
input: Input =>
val q = LotteryQuery().lotteryName.is(input.lotteryName)
val participantList = q.findOne.get.participantList //To find the participantList
val winnerIndex = scala.util.Random.nextInt(participantList.size) //To find a winner Index
val winner = participantList(winnerIndex)
val u = LotteryUpdate().winner.set(Some(winner)).open.set(false)
LotteryWriter().updateOne(q, u)
Output(winner)
}
}
- Implementing participantExist() and participantCount() in LotteryDomainCode
LotteryDomainCode.scala
override def lotteryNameExists (lotteryName:String):Boolean = LotteryQuery().lotteryName.equalsIgnoreCase(lotteryName).exists
override def participantExist(lotteryName: String, participantName: String): Boolean = LotteryQuery().lotteryName.is(lotteryName).participantList.contains(participantName).exists
override def participantCount(lotteryName: String): Int = LotteryQuery().lotteryName.is(lotteryName).findOne.get.participantList.size
def lotteryOpen (lotteryName:String):Boolean = (LotteryQuery().lotteryName.is(lotteryName).findOne.get.open)
- Add test for addParticipant and run in RunCommandTestSuite
- $project LoteryDomain
- $test-only com.metastay.lotterydomain.commandset.lottery.RunCommandTestSuite – -n run
Integrate Play and Domain¶
- Adding all the validations and checks which are available in domain to the rest api.
- Add LotteryDomain as a dependency to LotteryPlay Module rather than LotteryMongo in Lottery.ksmile
Lottery.ksmile
section Lottery {
mongo-module LotteryMongo at com.metastay.lotterymongo
domain-module LotteryDomain(LotteryMongo) at com.metastay.lotterydomain
play-module LotteryPlay(LotteryDomain) at lotteryplay
}
- $smile
- Add the command-action in place of action to link it to command in LotteryPlay.kplay
LotteryPlay.kplay
web-writer Lottery {
command-action createLottery(POST) LotteryDomain::Lottery.create
command-action addParticipant(POST) LotteryDomain::Lottery.addParticipant
command-action run(POST) LotteryDomain::Lottery.run
}
- $compile
- Delete the LotteryWebWriterCode.scala as its not more needed.
- $compile
- test through curl
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”Loto”,”amount”:1000}’ http://localhost:9000/api/lottery/create-lottery
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”Loto”,”participantName”:”John”}’ http://localhost:9000/api/lottery/add-participant
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”Loto”,”participantName”:”Jim”}’ http://localhost:9000/api/lottery/add-participant
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”Loto”,”participantName”:”Joe”}’ http://localhost:9000/api/lottery/add-participant
- curl -H “Content-Type: application/json” -X POST -d ‘{“lotteryName”:”Loto”}’ http://localhost:9000/api/lottery/run
Enhancement (change is easy)¶
- Add a pre to the command run, that if the lottery is closed than it cannot run.
- Add another function to domain Lottery in LotteryDomain.kdomain
LotteryDomain.kdomain
domain-logic Lottery {
function isOpenToRun(lotteryName:String):Boolean
}
* Add the pre to the run command
.. code-block:: scala
change Lottery command-set under LotteryDomain.kdomain
command run {
input(lotteryName:String);
pre {
condition NotYetRun => lottery.isOpenToRun(input.lotteryName) failing "it has already run!"
condition ExistingLotteryName => lottery.lotteryNameExists(input.lotteryName) failing "Lottery name does not exists"
condition atLeastTwoParticipants => ExistingLotteryName -> `domainLogicRef.lottery.participantCount(input.lotteryName) > 1` failing "The lottery does not have any participant!"
}
output(winner:String)
- $compile
- Implement LotteryDomainLogicCode isOpenToRun}
LotteryDomainLogicCode.scala
override def isOpenToRun(lotteryName: String): Boolean = LotteryQuery().lotteryName.is(lotteryName).open.is(false).exists
- run and test with swagger or curl