{"id":24840289,"url":"https://github.com/pzingg/cargo_shipping","last_synced_at":"2025-10-14T13:31:10.220Z","repository":{"id":46095063,"uuid":"422428800","full_name":"pzingg/cargo_shipping","owner":"pzingg","description":"A Phoenix LiveView interpretation of Eric Evans DDD example","archived":false,"fork":false,"pushed_at":"2021-11-23T18:03:22.000Z","size":1590,"stargazers_count":9,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-14T12:26:44.493Z","etag":null,"topics":["ddd","ddd-example","ddd-sample","elixir","eventbus","phoenix-liveview"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pzingg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-10-29T03:21:17.000Z","updated_at":"2024-08-13T03:43:24.000Z","dependencies_parsed_at":"2022-09-23T06:50:38.435Z","dependency_job_id":null,"html_url":"https://github.com/pzingg/cargo_shipping","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/pzingg/cargo_shipping","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pzingg%2Fcargo_shipping","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pzingg%2Fcargo_shipping/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pzingg%2Fcargo_shipping/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pzingg%2Fcargo_shipping/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pzingg","download_url":"https://codeload.github.com/pzingg/cargo_shipping/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pzingg%2Fcargo_shipping/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279018715,"owners_count":26086609,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ddd","ddd-example","ddd-sample","elixir","eventbus","phoenix-liveview"],"created_at":"2025-01-31T06:55:37.041Z","updated_at":"2025-10-14T13:31:09.308Z","avatar_url":"https://github.com/pzingg.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CargoShipping\n\nFrom the famous cargo shipping example in Eric Evans' 2003 book,\n\"Domain-Driven Design: Tackling Complexity in the Heart of Software\".\n\nLeverages code and patterns from:\n\n* https://github.com/citerus/dddsample-core - The original Java implementation.\n* https://github.com/pcmarks/ddd_elixir_demo_stage1 - A \"stage 1\" implementation in Elixir.\n\n## Installation\n\nTo start your Phoenix server:\n\n  * Install dependencies with `mix deps.get`\n  * Create, migrate and seed your database with `mix do ecto.reset, run priv/repo/seeds.exs`\n  * Start Phoenix endpoint with `mix phx.server` or inside IEx with `iex -S mix phx.server`\n\nNow you can visit [`localhost:4000`](http://localhost:4000) from your browser.\n\nReady to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html).\n\n## Learn more\n\n  * Official website: https://www.phoenixframework.org/\n  * Guides: https://hexdocs.pm/phoenix/overview.html\n  * Docs: https://hexdocs.pm/phoenix\n  * Forum: https://elixirforum.com/c/phoenix-forum\n  * Source: https://github.com/phoenixframework/phoenix\n\n## Central concepts and patterns\n\n1. Bounded contexts, like \"CargoBookings\" are implmented as Phoenix contexts.\n\n2. Aggregates and entities are implemented as Ecto Schemas, and their states\n  are saved in the PostgreSQL database.\n\n3. Value objects are implemented as embbeded schemas within the aggregates.\n\n4. Domain events are published using the\n  [EventBus](https://github.com/otobus/event_bus) library.\n\n5. Domain events are consumed asynchronously to update aggregates. Three\n  different event handlers are implemented as GenServers in the\n  `ApplicationEvents` namespace. A generic `Forwarder` event handler\n  is also used to subscribe to events in order to post notifications to\n  Phoenix LiveView processes to update the web interface.\n\n## Module architecture\n\n1. The project uses Sasa Juric's [boundary](https://github.com/sasa1977/boundary)\n  library to enforce interface and core layers. The main layers are:\n   * `CargoShippingWeb`, which depends on:\n   * `CargoShipping`, which depends on:\n   * `CargoShippingSchemas`, which defines value object and entity schemas.\n\n2. The project uses Phoenix contexts (`CargoBookings`, `Reports`, and `VoyagePlans`)\n  to contain core operations within Bounded Contexts.\n\n3. The project uses Service modules (based on the dddsample) to coordinate\n  complex operations.\n\n## Services\n\nTwo new services, `LocationService` and `VoyageService`, were added to\nthe original list of services from the original dddsample Java application.\n\n### LocationService\n\nFor performance reasons, a small subset of the UN \"locodes\" are maintained\nin memory rather than in the PostgreSQL database. Read-only queries of location\ndata are accessed via the `LocationService` module.\n\n### VoyageService\n\nSimilarly, since there are a small number of voyages in the sample code,\nan in-memory cache is maintained of all voyage data. Any modification of\nvoyage data in the database causes a reload of all voyages into the cache.\nQueries of voyage data access the cache via the `VoyageService` module.\n\n### CargoBookingService\n\n* book_new_cargo\n* request_possible_routes_for_cargo\n* assign_cargo_to_route\n* change_destination\n\n### CargoInspectionService\n\n* inspect_cargo\n\n### HandlingReportService\n\n* submit_report\n\n### HandlingEventService\n\n* register_handling_event\n* create_handling_event (added for testing)\n\n### RoutingService\n\n* fetch_routes_for_specification\n\n### GraphTraversalService\n\n* find_shortest_path\n* find_all_paths (added)\n\n## Itineraries\n\nTo better process unexpected handling events and maintain invariants, the schema for\nthe `Leg` objects which compose the `Itinerary` value object has been extended from\nthe original Java application to include the \"actual\" load and unload locations,\nand to include the current state of the leg (not started, completed, in transit, etc.).\n\n## Route finding implementation\n\nThe original Java implementation used a stub algorithm that generated random\nitineraries to re-route cargos. For a better implementation, this application\nuses the [libgraph](https://github.com/bitwalker/libgraph) Elixir library to search\nthe space of all available voyages to find the shortest sequence of voyage segments\nfor re-routing.\n\n\n## Web interface\n\nThe web interface, implemented using Phoenix LiveView, is based on the\nthe original Java templates at\n\nhttps://github.com/citerus/dddsample-core/tree/master/src/main/resources/templates\n\nThere are three Phoenix router scopes in the web application:\n\n### Shipping clerks web interface at /clerks\n\n* `get \"/\", PageController, :clerks` (clerks landing page)\n* `live \"/tracking\", CargoLive.Search, :index` (from track.html, to get information on\n  an existing cargo booking)\n\n### Operation managers web interface at /managers\n\n* `get \"/\", PageController, :managers` (managers landing page)\n* `live \"/cargos\", CargoLive.Index, :index` (from admin/list.html, to list all available\n  cargo bookings)\n* `live \"/cargos/new\", CargoLive.New, :new` (from admin/registrationForm.html, to create\n  a new cargo booking)\n* `live \"/cargos/id/:tracking_id\", CargoLive.Show, :show` (from admin/show.html, to show detailed\n  information on a cargo booking)\n* `live \"/cargos/id/:tracking_id/destination/edit\", CargoLive.EditDestination, :edit` (from\n  admin/pickNewDestination.html, to change the final destination for a cargo booking)\n* `live \"/cargos/id/:tracking_id/route/edit\", CargoLive.EditRoute, :edit` (from\n  admin/selectItinerary.html, to re-route a cargo booking)\n* `live \"/voyages\", VoyageLive.Search, :index` (to list all voyages or voyages by ports)\n* `live \"/voyages/new\", VoyageLive.New, :new` (to create a new voyage)\n* `live \"/voyages/number/:voyage_number\", VoyageLive.Show, :show` (to show information on\n  a voyage)\n* `live \"/events\", HandlingEventLive.Index, :all` (to list all recent handling events)\n* `live \"/events/id/:tracking_id\", HandlingEventLive.Index, :index` (to\n  list handling events for a cargo booking)\n* `live \"/reports/new\", HandlingReportLive.New, :new` (to file a cargo handling\n  report directly)\n\n### Handling Report REST API web interface at /api\n\n* `resources \"/handling_reports\", HandlingReportController` (to create a handling report\n  via an HTTP POST request)\n\n## Testing\n\nThe test at `cargo_lifecycle_scenario_test.exs` is closely based on the\n[CargoLifecycleScenarioTest.java](https://github.com/citerus/dddsample-core/blob/master/src/test/java/se/citerus/dddsample/scenario/CargoLifecycleScenarioTest.java)\nin the original Java implementation.\n\n## TODO\n\n* Add a search page for voyages\n\n* Apply all handling history events to a cargo (route specification + virgin itinerary).\n  To do this, we will probably need to save the cargo's original route specification\n  in the aggregate, and create new domain event topic(s) for route specification and/or\n  itinerary changes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpzingg%2Fcargo_shipping","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpzingg%2Fcargo_shipping","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpzingg%2Fcargo_shipping/lists"}