IvanR

IvanR

It was an elegant “Eventsourcing and CQRS in Elixir” talk given by @vasspilka at ElixirConf EU 2021. It shows how to use the Commanded library by @slashdotdash to model a Bank domain with the Domain-Driven Design approach and make an event-driven system out of it.

Having the well-defined model with Command and Event structs, it still can be an issue to validate data coming from the User Interface and validate the consistency of casting commands and events in each other.

For example, the value of the bank account identifier should be both of binary type and have a specific format. That is different from other kinds of binary identifiers across the system.

One of the possible ways to solve the validation problems defined above is by using Domo library in Command and Event structs like that:

# defining the format precondition for value's type 
defmodule Bank do
  import Domo

  @type account_number() :: binary()
  precond account_number: &validate_account_number/1

  def validate_account_number(string) do
    if string =~ ~r/^[[:digit:]]{3}-[[:digit:]]{3}$/, do: :ok, else: {:error, "Account number should be of xxx-xxx, x = 0..9 format."}
  end
  ...
end

# using Domo in the event struct
defmodule Bank.Core.Commands.DepositMoney do
  use Domo, ensure_struct_defaults: true
  ...
  # automatically adds new_ok/1 and new!/1 constructors
end

# using constructor in the Accounts context
defmodule Bank.Core.Accounts do
  def deposit_money(acc_id, amount) do
    [account_id: acc_id, amount: amount]
    |> DepositMoney.new_ok()
    |> maybe_dispatch(returning: :execution_result)
  end

  defp maybe_dispatch({:ok, command}, opts), do: Bank.Core.Application.dispatch(command, opts)
  defp maybe_dispatch({:error, _message} = error, _opts), do: error
  ...
end

Then the following errors can be handled automatically:

iex(1)> Accounts.deposit_money(nil, 10)        
{:error,
 [
   account_id: "Invalid value nil for field :account_id of %Bank.Core.Commands.DepositMoney{}. Expected the value matching the <<_::_*8>> type."
 ]}

iex(2)> Accounts.deposit_money("100102", 500)        
{:error, 
  [
    account_id: "Account number should be of xxx-xxx, x = 0..9 format."
  ]}

iex(3)> Accounts.deposit_money("100-102", -12)
{:error,
 [
   amount: "Invalid value -12 for field :amount of %Bank.Core.Commands.DepositMoney{}. Expected the value matching the non_neg_integer() type."
 ]}

iex(4)> Accounts.deposit_money("100-102", 120)
{:ok, %Commanded.Commands.ExecutionResult{}} 

Domo makes it possible to associate a precondition (format validation) function with the given @type definition and adds new_ok/1 validating constructor and several others to a struct.

The consistency of commands and events is ensured with the call to the validating constructor.

See the complete Bank example from the @vasspilka’s talk seasoned with Domo on GitHub - IvanRublev/bank-commanded-domo · GitHub.

More details about Domo Domo v1.5.19 — Documentation

Showing Posts 1 to 7

vasspilka

vasspilka

Thank you so much!! I feel like I could have done a better job at presenting, hopefully I’ll improve my speaking next for next time :wink:

Domo looks awesome, I was actually looking for something like it for a while, I wonder how well it can work with typed_struct I use it a lot in micro_words.

Thank’s again for the kind words, I’ll play around and explore Domo in the weekend and give you some feedback. Cheers :slight_smile:

IvanR

IvanR OP

Hey @vasspilka,

Thank you.

I like to use typed_struct too. The library is fully compatible :smile:
Goes down to throwing use Domo into the module and using new!/1 constructor then.

It’d be nice to get to know how Domo works in your project.

Sebb

Sebb

Where can I see the talk?

IvanR

IvanR OP

Thanks to Barbara Trojecka for the link to the video:

factoryd

factoryd

Nice talk!

I’ve been using Commanded for a few years now and have developed a small library to help with these (and other) issues as well.

It is based on Ecto embedded schemas as Ecto is pretty much ubiquitous in Elixir-land.

The library is called cqrs_tools and used in production on a few of my apps.

I saw this post and challenged myself to implement the project with cqrs_tools. So I forked the project mentioned in the OP and did it here.

The main strength of this library IMO is treating commands and queries as the smallest unit of execution while providing the tooling to take care of a common “lifecycle” for commands and queries; including input validation.

Commands need not be event-sourced but can be easily. They make no assumptions on how you execute them. They require a handle_dispatch callback to be implemented by the developer and that’s it.

This library also gives you a tool to build a “traditional” so-called Phoenix contexts via the BoundContext macro. Example here and here

I hope this is useful to someone and they can dig it. :slight_smile:

EDIT: There’s a couple livebooks in the repo to play around with as well.

IvanR

IvanR OP

Hey,

Cqrs_tool library looks suitable for automatizing the commands and events declarations even further!

And it seems that Domo library can be applied for format validation tasks with the cqrs_tool lib. F.e. in Cqrs.Command.handle_validate/2 callback.

Domo can benefit the project like the following:
it’s possible to define the @type for account_id and associate a format validation precondition function with it once. Then calling the ensure_type/1 on every command or event using Domo will automatically validate the appropriate field’s format. That works when you reference the account_id from the t() type definition of appropriate command or event.

Domo resolves all referencing types at compile time and builds runtime validation modules. The new/1 and ensure_type/1 functions in each struct using Domo call these validation modules then.

It’d be good to hear your feedback after combining Domo + cqrs_tool in a project :slightly_smiling_face:

factoryd

factoryd

Sorry for the delay.

I’ll have a good look at Domo as I’m currently busy writing a v1 of cqrs_tools.

— All posts loaded —

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 94592 917
New
cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
heathen
Quite interesting article Google brought me. Didn’t find any mentions about it here. What do you think in general? Would you use togethe...
New
AstonJ
Since we have deprecated our Erlang sections (as we have dedicated Erlang Forums now) let’s add this thread for those who’d like to post ...
New
maennchen
:warning: Security advisory: Decimal DoS vulnerability A vulnerability has been published for decimal where very large exponents can cau...
New
Null-logic-0
What IDE or editor are you using for Elixir development? Personally, I use Zed, and I really like it, but sometimes I wish there were a ...
New

Other Trending Topics Top

marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
webofbits
Aludel - LLM Evaluation Workbench Aludel is an embeddable Phoenix LiveView dashboard for evaluating and comparing LLM prompts across mult...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews