├── .github └── workflows │ └── docker-image.yml ├── .gitignore ├── .travis.yml ├── Account.pony ├── AccountRunner.pony ├── Contracts.pony ├── Dockerfile ├── README.md ├── Receiver.pony ├── SimplerAccount.pony ├── SimplerContracts.pony ├── SimplerReceiver.pony ├── docker-compose.yml └── test └── test_Account.pony /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Build the Docker image 18 | run: docker-compose up --build 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | DDDwithActorsPony 3 | *.exe 4 | *.exp 5 | *.ilk 6 | *.lib 7 | *.pdb 8 | *.obj 9 | *.o 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | services: 4 | - docker 5 | 6 | script: 7 | - docker-compose up --build 8 | -------------------------------------------------------------------------------- /Account.pony: -------------------------------------------------------------------------------- 1 | class State 2 | let account_number: String 3 | let balance: I64 4 | 5 | new create(account_number': String, balance': I64) => 6 | account_number = account_number' 7 | balance = balance' 8 | 9 | fun string(): String => 10 | "State account_number: "+account_number+" balance: "+ balance.string() 11 | 12 | 13 | 14 | actor Account is CommandProcessor 15 | let _env : Env 16 | var _state: State = State("",0) 17 | let _receiver: EventProcessor tag 18 | 19 | new create(env: Env, receiver: EventProcessor tag) => 20 | _receiver = receiver 21 | _env = env 22 | 23 | be process(command: Command val) => 24 | match command 25 | | let c: OpenAccount val => 26 | _state = State(c.account_number, c.balance) 27 | let state = _state 28 | _receiver.process(AccountOpened(state.account_number, state.balance)) 29 | 30 | | let c: DepositFunds val => 31 | _state = State(_state.account_number, _state.balance+c.amount) 32 | let state = _state 33 | _receiver.process(FundsDeposited(state.account_number, c.amount, state.balance)) 34 | 35 | | let c: WithdrawFunds val => 36 | _state = State(_state.account_number, _state.balance-c.amount) 37 | let state = _state 38 | _receiver.process(FundsWithdrawn(state.account_number, c.amount, state.balance)) 39 | end 40 | -------------------------------------------------------------------------------- /AccountRunner.pony: -------------------------------------------------------------------------------- 1 | actor Main 2 | new create(env: Env) => 3 | // version 1 4 | let printer = Receiver(env) 5 | var account = Account(env, printer) 6 | 7 | account.process(OpenAccount("A-1234",100)) 8 | account.process(DepositFunds(50)) 9 | account.process(WithdrawFunds(75)) 10 | 11 | // version 2 12 | let simpler_receiver = SimplerReceiver(env) 13 | let simpler_account = SimplerAccount("A-5678", 100, simpler_receiver) 14 | simpler_account.deposit_funds(50) 15 | simpler_account.withdraw_funds(75) -------------------------------------------------------------------------------- /Contracts.pony: -------------------------------------------------------------------------------- 1 | class val OpenAccount 2 | let account_number: String 3 | let balance: I64 4 | 5 | new val create(account_number': String, balance': I64) => 6 | account_number = account_number' 7 | balance = balance' 8 | 9 | class val DepositFunds 10 | let amount: I64 11 | 12 | new val create(amount': I64) => 13 | amount = amount' 14 | 15 | 16 | class val WithdrawFunds 17 | let amount: I64 18 | 19 | new val create(amount': I64) => 20 | amount = amount' 21 | 22 | 23 | type Command is (OpenAccount | DepositFunds | WithdrawFunds) 24 | 25 | trait CommandProcessor 26 | be process(command: Command val) 27 | 28 | 29 | class val AccountOpened 30 | let account_number: String 31 | let initial_balance: I64 32 | 33 | new val create(account_number': String, initial_balance': I64) => 34 | account_number = account_number' 35 | initial_balance = initial_balance' 36 | 37 | fun string(): String => 38 | "AccountOpened account_number: "+account_number+", initial_balance: "+initial_balance.string() 39 | 40 | 41 | class val FundsDeposited 42 | let account_number: String 43 | let amount: I64 44 | let balance: I64 45 | 46 | new val create(account_number': String, amount': I64, balance': I64) => 47 | account_number = account_number' 48 | amount = amount' 49 | balance = balance' 50 | 51 | fun string(): String => 52 | account_number + ": FundsDeposited amount: "+amount.string()+", balance: "+balance.string() 53 | 54 | 55 | class val FundsWithdrawn 56 | let account_number: String 57 | let amount: I64 58 | let balance: I64 59 | 60 | new val create(account_number': String, amount': I64, balance': I64) => 61 | account_number = account_number' 62 | amount = amount' 63 | balance = balance' 64 | 65 | fun string(): String => 66 | account_number + ": FundsWithdrawn amount: "+amount.string()+", balance: "+balance.string() 67 | 68 | type Events is (AccountOpened | FundsDeposited | FundsWithdrawn) 69 | 70 | trait EventProcessor 71 | be process(event: Events val) 72 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ponylang/ponyc:release 2 | 3 | COPY . /src/main/ 4 | WORKDIR /src/main 5 | RUN ponyc 6 | CMD ./main --ponythreads=4 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DDDwithActorsPony 2 | 3 | [![Docker Image CI](https://github.com/d-led/DDDwithActorsPony/actions/workflows/docker-image.yml/badge.svg)](https://github.com/d-led/DDDwithActorsPony/actions/workflows/docker-image.yml) 4 | 5 | Pony version of https://github.com/VaughnVernon/DDDwithActors by Vaughn Vernon 6 | 7 | (no persistence at the moment) 8 | 9 | ## Structure 10 | 11 | - main: [AccountRunner.pony](AccountRunner.pony) 12 | - contracts: [Contracts.pony](Contracts.pony) 13 | - contracts for a second, type-minimizing version: [SimplerContracts.pony](SimplerContracts.pony) 14 | - implementations: the rest 15 | 16 | ## Running 17 | 18 | - `docker-compose up --build` 19 | - alternative: `ponyc && ./DDDwithActorsPony` 20 | -------------------------------------------------------------------------------- /Receiver.pony: -------------------------------------------------------------------------------- 1 | actor Receiver is EventProcessor 2 | let _env : Env 3 | 4 | new create(env: Env) => 5 | _env = env 6 | 7 | be process(event: Events val) => 8 | _env.out.print(event.string()) 9 | -------------------------------------------------------------------------------- /SimplerAccount.pony: -------------------------------------------------------------------------------- 1 | actor SimplerAccount 2 | let _account_number: String 3 | let _receiver: SimplerEventProcessor tag 4 | 5 | var _balance: I64 6 | 7 | new create(account_number: String, 8 | balance: I64, 9 | receiver: SimplerEventProcessor tag) => 10 | _receiver = receiver 11 | _account_number = account_number 12 | _balance = balance 13 | 14 | open_account() 15 | 16 | fun open_account() => 17 | _receiver.account_opened(_account_number, _balance) 18 | 19 | be deposit_funds(amount: I64) => 20 | _balance = _balance + amount 21 | _receiver.funds_deposited(_account_number, amount, _balance) 22 | 23 | be withdraw_funds(amount: I64) => 24 | _balance = _balance - amount 25 | _receiver.funds_withdrawn(_account_number, amount, _balance) 26 | -------------------------------------------------------------------------------- /SimplerContracts.pony: -------------------------------------------------------------------------------- 1 | trait SimplerEventProcessor 2 | be account_opened(account_number: String, initial_balance: I64) 3 | be funds_deposited(account_number: String, amount: I64, balance: I64) 4 | be funds_withdrawn(account_number: String, amount: I64, balance: I64) 5 | -------------------------------------------------------------------------------- /SimplerReceiver.pony: -------------------------------------------------------------------------------- 1 | actor SimplerReceiver is SimplerEventProcessor 2 | let _env : Env 3 | 4 | new create(env: Env) => 5 | _env = env 6 | 7 | be account_opened(account_number: String, initial_balance: I64) => 8 | _env.out.print("AccountOpened account_number: "+account_number+", initial_balance: "+initial_balance.string()) 9 | 10 | be funds_deposited(account_number: String, amount: I64, balance: I64) => 11 | _env.out.print(account_number + ": FundsDeposited amount: "+amount.string()+", balance: "+balance.string()) 12 | 13 | be funds_withdrawn(account_number: String, amount: I64, balance: I64) => 14 | _env.out.print(account_number + ": FundsWithdrawn amount: "+amount.string()+", balance: "+balance.string()) 15 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | pony: 4 | build: 5 | context: . 6 | -------------------------------------------------------------------------------- /test/test_Account.pony: -------------------------------------------------------------------------------- 1 | use "ponytest" 2 | use "promises" 3 | 4 | use sut = ".." 5 | 6 | actor Main is TestList 7 | new create(env: Env) => 8 | PonyTest(env, this) 9 | 10 | fun tag tests(test: PonyTest) => 11 | test(_TestAccountEvents) 12 | 13 | class iso _TestAccountEvents is UnitTest 14 | var balance_promise: Promise[I64] = Promise[I64] 15 | 16 | fun name(): String => "account events" 17 | 18 | 19 | fun apply(h: TestHelper) => 20 | h.long_test(1_000_000_000) 21 | 22 | let rec = TestReceiver(balance_promise) 23 | 24 | let acc = sut.SimplerAccount("A-5678", 100, rec) 25 | expect_balance_to_be(h, 100) 26 | 27 | acc.deposit_funds(50) 28 | expect_balance_to_be(h, 150) 29 | 30 | acc.withdraw_funds(75) 31 | expect_balance_to_be(h, 75, true) 32 | 33 | 34 | fun expect_balance_to_be(h: TestHelper, balance': I64, finished: Bool = false) => 35 | balance_promise.next[I64](recover this~assert_balance_eq(h, balance') end) 36 | 37 | if finished then 38 | h.complete(true) 39 | end 40 | 41 | 42 | fun tag assert_balance_eq(h: TestHelper, expected_balance: I64, balance': I64): I64 => 43 | h.assert_eq[I64](expected_balance, balance') 44 | balance' 45 | 46 | fun timed_out(h: TestHelper) => 47 | h.complete(false) 48 | 49 | 50 | actor TestReceiver is sut.SimplerEventProcessor 51 | let _balance_promise: Promise[I64] 52 | 53 | new create(promise: Promise[I64]) => 54 | _balance_promise = promise 55 | 56 | be account_opened(account_number: String, initial_balance: I64) => 57 | _balance_promise(initial_balance) 58 | 59 | be funds_deposited(account_number: String, amount: I64, balance': I64) => 60 | _balance_promise(balance') 61 | 62 | be funds_withdrawn(account_number: String, amount: I64, balance': I64) => 63 | _balance_promise(balance') 64 | --------------------------------------------------------------------------------