Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
  • Jan 30 2019 14:49
    slashdotdash edited #169
  • Jan 30 2019 14:48
    Freyskeyd commented #249
  • Jan 30 2019 14:48
    Freyskeyd closed #249
  • Jan 30 2019 10:28
    slashdotdash commented #249
  • Jan 30 2019 09:08
    Freyskeyd commented #249
  • Jan 29 2019 11:37
    Freyskeyd commented #187
  • Jan 29 2019 11:34
    Freyskeyd synchronize #249
  • Jan 29 2019 10:50
    Freyskeyd synchronize #249
  • Jan 29 2019 10:16
    slashdotdash closed #231
  • Jan 29 2019 10:16
    slashdotdash commented #231
  • Jan 29 2019 09:48
    Freyskeyd commented #231
  • Jan 29 2019 09:10
    Freyskeyd synchronize #249
  • Jan 28 2019 15:49
    Freyskeyd edited #249
  • Jan 28 2019 15:32
    Freyskeyd synchronize #249
  • Jan 28 2019 15:04
    Freyskeyd opened #249
  • Jan 28 2019 11:20
    imetallica closed #119
  • Jan 28 2019 11:20
    imetallica commented #119
  • Jan 28 2019 10:40
    Freyskeyd commented #119
  • Jan 28 2019 10:40
    slashdotdash commented #184
  • Jan 28 2019 10:39
    Freyskeyd commented #52
Ben Smith
@slashdotdash
@grkek Are you asking for my help, or venting your frustration?
Giorgi Kavrelishvili
@grkek
Both :D
How does one improve the performance of Commanded?
Ben Smith
@slashdotdash

It’s difficult to give guidance without knowing exactly what your domain is. Usually you want to design small, short lived aggregates with few events in their lifetime.

You can partition your application by service, context, tenant, etc. to improve performance by using a separate Commanded Application and EventStore for each. This will help to distribute load, processing, and event store read/writes.

I’ve written up the various architectural choices available.

https://10consulting.com/2021/03/18/commanded-application-architecture/

If you can identify a particular bottleneck in Commanded itself then that can be looked into.
Ben Smith
@slashdotdash
Dispatching commands with consistency: :strong will add latency if used due to blocking until the event(s) have been handled by any configured event handlers. The default event consistency is recommended for low latency.
Tomek Rybczyński
@rybex
@gugl_twitter Hi. Thanks for your previous tip. It helped a lot :) I have one more question. How to check Commanded.ProcessManagers.ProcessManager state? Do you have any example of how to load process manager state locally?
Günter Glück
@gugl_twitter
@rybex Glad it helped. For debugging purposes or something like that?
Tomek Rybczyński
@rybex
@gugl_twitter yes debugging only :)
Günter Glück
@gugl_twitter
@rybex I see. Unfortunately I never tried that but you could try to find a way to start/get the https://github.com/commanded/commanded/blob/master/lib/commanded/process_managers/process_manager_instance.ex you want to investigate state for and find a way to use the process_state function on it but it might be easier to use the telemetry for it. Maybe that is a useful pointer and the better way to go for it: test/process_managers/process_manager_telemetry_test.exs I think I would follow the telemetry path for debugging purposes.
narayan iyer
@iyerland
@slashdotdash When is an aggregate removed from memory (or persisted) and what is done to get it back in memory? Do you re-play all the events in the event_store to re-construct the aggregate?
Ben Smith
@slashdotdash

Aggregate instances persist in memory indefinitely unless you implement the Commanded.Aggregates.AggregateLifespan behaviour.

https://hexdocs.pm/commanded/Commanded.Aggregates.AggregateLifespan.html

An instance is started and its state is rebuilt from either a state snapshot + new events or all historical events on demand when a command is dispatched to it.

narayan iyer
@iyerland
Thanks @slashdotdash
Tomek Rybczyński
@rybex
@gugl_twitter Thx for your response :) I will try that
Marcelo Dominguez
@marpo60
Is it possible to add and remove middleware dynamically?
Ben Smith
@slashdotdash
Not currently as the middleware is configured at compile-time, so it is fixed
But I dont want to add that middleware in the production code, only on some tests.
Ben Smith
@slashdotdash
You could use a compile-time conditional to include the middleware only when the env is :test (configured in config/test.exs):
if Application.compile_env(:my_app, :env) == :test do
  middleware CommandAuditMiddleware
end
# config/test.exs
config :my_app, env: :test
Or use a similar environment check inside the middleware module / functions
Marcelo Dominguez
@marpo60
yeah, that would work.
Thanks Ben for the help and the fast reply!
Ben Smith
@slashdotdash
def before_dispatch(pipeline, env \\ Application.compile_env(:my_app, :env))
def before_dispatch(pipeline, :test) do
  # auditing

  pipeline
end
def before_dispatch(pipeline, _env), do: pipeline
mlchai
@mlchai
sorry if this is a dumb question (new to ES and commanded) but if I wanted an aggregate root to talk to other aggregates (check their state or send messages to them to emit events) what would be the best way of doing something like that?
Daniel Drexler
@aeturnum

Hey there - I'm trying to get a basic commanded setup and I'm getting a confusing error at the eventstore <-> postgrex connection in a basic test:

The full gory traceback is available here, but it's happening after an aggregate returns an event to be stored in postgres.

It seems to be failing to match on this line of (appender.ex)[https://github.com/commanded/eventstore/blob/master/lib/event_store/storage/appender.ex]:
%Postgrex.Error{postgres: %{code: error_code, constraint: constraint}} = error

The Postgres.Error.postgres map does not have a :constraint field

Does this ring any bells for folks?

(commanded 1.2.0, commanded_eventstore_adapter 1.2.0, evenstore 1.3.1, postgrex 0.15.9)

lee eggebroten
@leggebroten

There's a pretty common Use Case that I'm not finding a "clean" solution for. I've a simple Commanded change in mind, but am tossing this out to see if I'm missing something obvious ...

Use Case:
a) For any given Command, notify external client(s) whenever any of the Events produced by the Handler alters the Aggregate's state
b) ONLY ONE notification can be emitted regardless of the number of that Command's Events which alter Aggregate state.
c) NO notification can be emitted if the Command's Events made no changes to the Aggregate
d) The notification should be provided with the Aggregate's delta produced by the Command Handler's Events.

Observations:

  • (The most basic ...) Events notify a set of Listeners/Subscribers that "something happened"
  • A subclass of Events change an Aggregate's state. Mutators.
  • This Use Case is illustrating another sub-class of Event that plays the initiator role in the Observer pattern. For clarity I'll refer to them as "Observers" even though they're not themselves Observers. (I don't have a better name for this role they're playing)
  • For the pattern to work an Observer cannot also be a Mutator
  • The ordering of Mutator Events is important.
  • The ordering of Observer Events is irrelevant so long as they follow all Mutator Events.

There are three architecturally clumsy solutions I've thought of (none of which can meet d):
1) Specifically append an Observer Event at the end of every Handler which has one or more Mutator Events. This means the Handler is Coupled to the implementation details of all its listed events. A fragile solution vulnerable to any changes to Events (or require costly tests).
2) Delegate to the Mutator modules the task of appending Observer Events Unfortunately, to meet b, this would require Coupling Events with its siblings (to avoid sending multiple notifications)
3) An Event Handler or Process Manager could somehow trigger the notification, but there's no means of detecting when all the Events from a particular Command have finished meaning this would likely not be able to meet b.

Thoughts?

Ben Smith
@slashdotdash
@aeturnum Which version of Postgres are you using? You might need to upgrade to v10 or newer.
Ben Smith
@slashdotdash
@leggebroten You could have the aggregate emit a StateChanged event whenever the state is mutated by the events produced from a command by comparing the aggregate state before and after the command’s event(s) have been applied. You may be able to to use the Commanded.Aggregate.Multi approach here. The StateChanged event could contain fields with the state before and after or you could determine what has changed and include a delta.
The benefit of using an event is that it gets included in the event log as an immutable fact. Great for auditing/debugging
lee eggebroten
@leggebroten

@slashdotdash
Thanks for getting back to me.
Emitting a StateChanged event is what I'm doing now. Works but is kinda clunky 'cause the Command Handler has to know that one or more of the Events it's emitting will induce a state change.

Not the end of the world ...

Tran Manh Linh
@manhtranlinh
Hi all, I have just found a issues:
if I config in confix.exs the parameter "config :commanded, default_consistency: :strong", it work exactly and I can query the read model after dispatch command return :ok
But if I use consistency: :strong option in dispatch command directly, although dispatch return :ok but it will not work smoothly: few cases work, few cases fail, because the query to read model run before projection complete.
Benjamin Moss
@drteeth
You have to include consistency: strong on each read model you want to be strong as well as dispatch the command with the strong option
@manhtranlinh ^^
Tran Manh Linh
@manhtranlinh
Thanks @drteeth , it work, I did not know that can config "consistency: :strong" for read model :D
Tran Manh Linh
@manhtranlinh
I am working with a problem where command and event are a long, nested structures. I use Ecto.Schema and Ecto.Changeset to create command and in Aggregate I used: command = Map.from_struct(command) |> Map.drop([:meta, :associations_name]), for nested structures I also convent them to a map and update command map after that I used struct(Event, command) to make event .
Although it work but I am confused that whether this way is okay or not?
Scott Ming
@scottming
I think using Ecto to create command is not good.
Tran Manh Linh
@manhtranlinh

I think using Ecto to create command is not good.

Could you please share more about your experience?

Scott Ming
@scottming
michelbieleveld
@michelbieleveld

@manhtranlinh I am doing the same but only the validation part of ecto
to create the command

defmodule Acai.IAM.Commands.RegisterTenant do
  use Ecto.Schema
  import Ecto.Changeset
  use Bitwise
  import Acai.IAM.Validators.GenerateUUID

  @derive {Jason.Encoder, except: [:tenant_id, :name, :account, :email, :password]}
  @primary_key false
  embedded_schema do
    field :tenant_id, :binary_id
    field :name, :string
    field :account, :string
    field :email, :string
    field :password, :string
  end

  alias Acai.IAM.Commands.RegisterTenant

  def changeset(%RegisterTenant{} = tenant, attrs \\ %{}) do
    required_fields = [:name, :email, :password]
    optional_fields = [:tenant_id]

    tenant
    |> cast(attrs, required_fields ++ optional_fields)
    |> validate_required(required_fields)
    |> validate_format(:email, ~r/^((?!@)\S)+[@]((?!@)\S)+/)
    |> validate_length(:password, min: 6)
    |> generate_uuid(:tenant_id)
    |> generate_account_id()
  end

to use the command:

defmodule Acai.IAM.Aggregates.Tenant do
  use Ecto.Schema

  @primary_key {:id, :binary_id, autogenerate: false}

  schema "tenants" do
    field :name, :string
    field :alias, :string
    field :account, :string
    timestamps()
  end

  alias Acai.IAM.Aggregates.Tenant

  alias Acai.IAM.Commands.{
    RegisterTenant
  }

  alias Acai.IAM.Events.{
    TenantRegistered
  }

  def execute(%Tenant{id: nil}, %RegisterTenant{} = register) do
    %TenantRegistered{
      tenant_id: register.tenant_id,
      name: register.name,
      account: register.account,
      email: register.email,
      password: register.password
    }
  end

  def apply(%Tenant{} = tenant, %TenantRegistered{} = registered) do
    %Tenant{
      tenant
      | id: registered.tenant_id,
        name: registered.name,
        account: registered.account
    }
  end
end

then the actual writing to the database is done through projections. Kicking of the command and validations only on input data, so no querying the data:

defmodule Acai.IAM.Interface.TenantInterface do
  import Ecto.Changeset
  alias Acai.{App, Repo}
  alias Acai.IAM.Commands.RegisterTenant

  def register_tenant(attrs \\ %{}) do
    register_tenant =
      %RegisterTenant{}
      |> RegisterTenant.changeset(attrs)

    with true <- register_tenant.valid?(),
         id <- UUID.uuid4(),
         :ok <- App.dispatch(apply_changes(register_tenant), correlation_id: id) do
      {:ok, %{command_uuid: id}}
    else
      false -> {:error, Repo.error_messages(register_tenant)}
      res -> res
    end
  end
end

This is working, but mentioning that I just started with elixir and commanded so can't give you production experience.

michelbieleveld
@michelbieleveld
The projection
defmodule Acai.IAM.Projectors.Tenant do
  use Commanded.Projections.Ecto,
    application: Acai.App,
    name: "IAM.Projectors.Tenant",
    consistency: :strong

  alias Acai.IAM.Events.{
    TenantRegistered
  }

  alias Acai.IAM.Projections.Tenant

  project(%TenantRegistered{} = registered, fn multi ->
    Ecto.Multi.insert(multi, :tenant, %Tenant{
      id: registered.tenant_id,
      name: registered.name,
      account: registered.account
    })
  end)

  @impl Commanded.Projections.Ecto
  def after_update(event, metadata, _changes) do
    event |> IO.inspect()
    metadata |> IO.inspect()
    # Use the event, metadata, or `Ecto.Multi` changes and return `:ok`
    :ok
  end
end
Conduit uses Vex for validations and commands , Ecto has a similar functionality.
Alexander Zinolidis
@alexanderzinolidis
how can a process manager dispatch commands with consistency: :strong?
or am I thinking about it the wrong way?
Ben Smith
@slashdotdash
@alexanderzinolidis A process manager cannot dispatch a command with strong consistency. A process manager is meant to orchestrate multiple aggregates which means we can only guarantee eventual consistency (since each aggregate is its own transaction/consistency boundary).
Alexander Zinolidis
@alexanderzinolidis
How can I write a test that expects a read side projection to be created (as a result of a PM dispatching a command)?
Ben Smith
@slashdotdash
I do the following: commanded/recipes#18
Implement the after_update/3 callback in the projector and emit a telemetry event. The test can then attach a handler to the telemetry event, send the test process a message, and use assert_receive to wait until the message is received.
Alexander Zinolidis
@alexanderzinolidis
oh, great! Thanks
Ben Smith
@slashdotdash
I think message sending and receive/assert_receive are the idiomatic Elixir/Erlang way of communicating between processes which is also useful for blocking and waiting in tests.
Alexander Zinolidis
@alexanderzinolidis
agreed, seems like a good solution