├── .editorconfig
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── behat.yml
├── bin
├── composer
├── install
└── run_tests
├── bootstrap.php
├── cache
└── .gitkeep
├── composer.json
├── composer.lock
├── docker-compose.yml
├── docker
└── php
│ ├── Dockerfile
│ └── app.ini
├── phpstan-baseline.neon
├── phpstan.neon
├── phpunit.xml.dist
├── run_tests.sh
├── src
└── TicketMill
│ ├── Application
│ ├── CancelReservation.php
│ ├── MakeReservation.php
│ ├── Notifications
│ │ ├── Mailer.php
│ │ └── SendMail.php
│ └── PlanConcert.php
│ ├── Domain
│ └── Model
│ │ ├── Common
│ │ ├── EmailAddress.php
│ │ └── EventRecording.php
│ │ ├── Concert
│ │ ├── Concert.php
│ │ ├── ConcertId.php
│ │ ├── ConcertRepository.php
│ │ ├── ConcertWasPlanned.php
│ │ ├── CouldNotFindConcert.php
│ │ ├── CouldNotFindReservation.php
│ │ ├── CouldNotRescheduleConcert.php
│ │ ├── CouldNotReserveSeats.php
│ │ ├── Reservation.php
│ │ ├── ReservationId.php
│ │ ├── ReservationWasCancelled.php
│ │ ├── ReservationWasMade.php
│ │ └── ScheduledDate.php
│ │ └── Reservation
│ │ ├── Reservation.php
│ │ └── ReservationRepository.php
│ └── Infrastructure
│ ├── EventSubscriberSpy.php
│ ├── InMemoryConcertRepository.php
│ ├── InMemoryReservationRepository.php
│ ├── MailerSpy.php
│ └── ServiceContainer.php
└── test
├── Acceptance
├── FeatureContext.php
└── features
│ └── reserving_seats.feature
└── Unit
├── TicketMill
├── Domain
│ └── Model
│ │ ├── Common
│ │ └── EmailAddressTest.php
│ │ ├── Concert
│ │ ├── ConcertIdTest.php
│ │ ├── ConcertTest.php
│ │ ├── ReservationIdTest.php
│ │ └── ScheduledDateTest.php
│ │ └── Reservation
│ │ └── ReservationTest.php
└── Infrastructure
│ ├── InMemoryConcertRepositoryTest.php
│ ├── InMemoryReservationRepositoryTest.php
│ ├── MailerSpyTest.php
│ └── ServiceContainerTest.php
└── Utility
├── AggregateTestCase.php
├── AggregateTestCaseTest.php
├── ArrayContainsObjectOfClass.php
└── Dummy.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | charset = utf-8
7 |
8 | [*.{php,json,yaml,yml}]
9 | indent_style = space
10 | indent_size = 4
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | /.idea/
3 | /.phpunit.result.cache
4 | /cache/
5 | /.env
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 |
3 | services:
4 | - docker
5 |
6 | install:
7 | - bin/install
8 |
9 | script:
10 | - bin/run_tests
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) Matthias Noback
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Aggregate design workshop
2 |
3 | ## Requirements
4 |
5 | - [Docker Engine](https://docs.docker.com/engine/installation/)
6 | - [Docker Compose](https://docs.docker.com/compose/install/)
7 |
8 | ## Getting started
9 |
10 | - Clone this repository and `cd` into it.
11 | - Run `bin/install`, which will pull the relevant Docker images and run `composer install`
12 |
13 | ## Usage
14 |
15 | - Run `bin/composer` to use Composer (e.g. `bin/composer require --dev [...]`).
16 | - Run `bin/run_tests` to run the tests.
17 |
--------------------------------------------------------------------------------
/behat.yml:
--------------------------------------------------------------------------------
1 | default:
2 | suites:
3 | acceptance:
4 | paths: ["%paths.base%/test/Acceptance/features"]
5 | contexts:
6 | - Test\Acceptance\FeatureContext
7 |
--------------------------------------------------------------------------------
/bin/composer:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | docker compose run --rm composer --ignore-platform-reqs "$@"
4 |
--------------------------------------------------------------------------------
/bin/install:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Copy user and group ID into .env file
4 | printf "HOST_UID=%s\nHOST_GID=%s\n" "$(id -u)" "$(id -g)" > .env
5 |
6 | # Pull Docker images
7 | docker compose pull
8 |
9 | # Run composer install
10 | docker compose run --rm composer install --ignore-platform-reqs --prefer-dist
11 |
--------------------------------------------------------------------------------
/bin/run_tests:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | docker compose run --rm php ./run_tests.sh
4 |
--------------------------------------------------------------------------------
/bootstrap.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | src/
6 |
7 |
8 |
9 |
10 | ./test/Unit
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/run_tests.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | set -eu
4 |
5 | vendor/bin/phpstan analyse
6 | vendor/bin/phpunit --testsuite unit
7 | #vendor/bin/behat --suite acceptance --tags "~@ignore"
8 |
--------------------------------------------------------------------------------
/src/TicketMill/Application/CancelReservation.php:
--------------------------------------------------------------------------------
1 | concertRepository = $concertRepository;
21 | $this->eventDispatcher = $eventDispatcher;
22 | }
23 |
24 | public function cancelReservation(string $concertId, string $reservationId): void
25 | {
26 | $concert = $this->concertRepository->getById(
27 | ConcertId::fromString($concertId)
28 | );
29 |
30 | $concert->cancelReservation(ReservationId::fromString($reservationId));
31 |
32 | $this->concertRepository->save($concert);
33 |
34 | $this->eventDispatcher->dispatchAll($concert->releaseEvents());
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/TicketMill/Application/MakeReservation.php:
--------------------------------------------------------------------------------
1 | concertRepository = $concertRepository;
22 | $this->eventDispatcher = $eventDispatcher;
23 | }
24 |
25 | public function makeReservation(string $concertId, string $emailAddress, int $numberOfSeats): ReservationId
26 | {
27 | $concert = $this->concertRepository->getById(ConcertId::fromString($concertId));
28 |
29 | $reservationId = $this->concertRepository->nextReservationId();
30 |
31 | $concert->makeReservation(
32 | $reservationId,
33 | EmailAddress::fromString($emailAddress),
34 | $numberOfSeats
35 | );
36 |
37 | $this->concertRepository->save($concert);
38 |
39 | $this->eventDispatcher->dispatchAll($concert->releaseEvents());
40 |
41 | return $reservationId;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/TicketMill/Application/Notifications/Mailer.php:
--------------------------------------------------------------------------------
1 | mailer = $mailer;
15 | }
16 |
17 | public function whenReservationWasMade(ReservationWasMade $reservationWasMade): void
18 | {
19 | $this->mailer->sendReservationWasMadeEmail(
20 | $reservationWasMade->emailAddress(),
21 | $reservationWasMade->numberOfSeats()
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/TicketMill/Application/PlanConcert.php:
--------------------------------------------------------------------------------
1 | concertRepository = $concertRepository;
22 | $this->eventDispatcher = $eventDispatcher;
23 | }
24 |
25 | public function plan(
26 | string $name,
27 | string $date,
28 | int $numberOfSeats
29 | ): ConcertId {
30 | $concert = Concert::plan(
31 | $this->concertRepository->nextIdentity(),
32 | $name,
33 | ScheduledDate::fromString($date),
34 | $numberOfSeats
35 | );
36 |
37 | $this->concertRepository->save($concert);
38 |
39 | $this->eventDispatcher->dispatchAll($concert->releaseEvents());
40 |
41 | return $concert->concertId();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/TicketMill/Domain/Model/Common/EmailAddress.php:
--------------------------------------------------------------------------------
1 | emailAddress = $emailAddress;
16 | }
17 |
18 | public static function fromString(string $emailAddress): EmailAddress
19 | {
20 | return new self($emailAddress);
21 | }
22 |
23 | public function asString(): string
24 | {
25 | return $this->emailAddress;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/TicketMill/Domain/Model/Common/EventRecording.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | private array $events = [];
14 |
15 | final private function recordThat(object $event): void
16 | {
17 | Assertion::isObject($event, 'An event should be an object');
18 |
19 | $this->events[] = $event;
20 | }
21 |
22 | /**
23 | * @return array