# `Chronicle.Events.ConcurrencyScope`
[🔗](https://github.com/Cratis/Chronicle.Elixir/blob/main/lib/chronicle/events/concurrency_scope.ex#L4)

Represents a concurrency scope for Chronicle append operations.

Concurrency scopes let you assert the expected tail sequence number before
appending one or more events. You can optionally narrow the check to the
current event source, stream, source type, and event types.

    alias Chronicle.Events.ConcurrencyScope

    scope =
      ConcurrencyScope.for_event_source(3,
        event_types: [MyApp.Events.FundsDeposited, MyApp.Events.FundsWithdrawn]
      )

    :ok = Chronicle.append_many("account-42", [...], concurrency_scope: scope)

The `event_source_id` option is a boolean because the actual event source id
comes from the `Chronicle.append/3` or `Chronicle.append_many/3` call.

# `event_type_module`

```elixir
@type event_type_module() :: module()
```

# `option`

```elixir
@type option() ::
  {:event_source_id, boolean()}
  | {:event_stream_type, String.t() | atom() | nil}
  | {:event_stream_id, String.t() | atom() | nil}
  | {:event_source_type, String.t() | atom() | nil}
  | {:event_types, [event_type_module()] | nil}
  | {:expects_no_matching_event, boolean()}
```

# `t`

```elixir
@type t() :: %Chronicle.Events.ConcurrencyScope{
  event_source_id: boolean(),
  event_source_type: String.t(),
  event_stream_id: String.t(),
  event_stream_type: String.t(),
  event_types: [event_type_module()],
  expects_no_matching_event: boolean(),
  sequence_number: non_neg_integer()
}
```

# `at_tail`

```elixir
@spec at_tail(String.t(), keyword()) :: {:ok, t()} | {:error, term()}
```

Reads the current tail sequence number for `event_source_id` - narrowed the same way the
returned scope narrows, via `:event_stream_type`, `:event_stream_id`, `:event_source_type`
and `:event_types` - and returns a scope expecting that tail. A live-read shortcut for
`for_event_source(tail, opts)`, mirroring .NET's `ConcurrencyScopeBuilder` combined with a
tail read (the pattern the optimistic strategy itself follows).

    {:ok, scope} = ConcurrencyScope.at_tail("account-1", event_stream_type: "Transactions")
    Chronicle.append("account-1", %FundsDeposited{amount: 10}, concurrency_scope: scope)

Accepts `:client`, `:namespace` and `:event_sequence_id` in addition to the narrowing
options above. Returns `{:error, reason}` when the tail read itself fails.

# `expecting_no_matching_event`

```elixir
@spec expecting_no_matching_event([option()] | t()) :: t()
```

Creates a scope asserting that **no** event matches the scope's dimensions yet - the guard for
creation-only operations ("this event source must not have been registered already").

Mirrors .NET's `ConcurrencyScopeBuilder.ExpectingNoMatchingEvent()`. It sends the kernel's explicit
no-matching-event condition; leaving the sequence number unset is not a substitute, because
that disables the check instead of asserting anything. The narrowing options are those of
`new/2` - typically the event source and the event types that would make the append invalid:

    scope =
      ConcurrencyScope.expecting_no_matching_event(
        event_source_id: true,
        event_types: [MyApp.Events.UserRegistered]
      )

    Chronicle.append("user-1", %UserRegistered{}, concurrency_scope: scope)

# `for_event_source`

```elixir
@spec for_event_source(non_neg_integer(), [option()]) :: t()
```

Creates a concurrency scope that includes the current append event source id.

# `new`

```elixir
@spec new(non_neg_integer(), [option()]) :: t()
```

Creates a new concurrency scope.

# `none`

```elixir
@spec none() :: t()
```

Returns the sentinel scope representing no explicit concurrency constraint.

# `with_event_source`

```elixir
@spec with_event_source(t()) :: t()
```

Includes the current append's event source id in the scope. Mirrors .NET's
`ConcurrencyScopeBuilder.WithEventSourceId()`.

# `with_event_source_type`

```elixir
@spec with_event_source_type(t(), String.t() | atom()) :: t()
```

Narrows the scope to a specific event source type. Mirrors .NET's
`ConcurrencyScopeBuilder.WithEventSourceType()`.

# `with_event_stream_id`

```elixir
@spec with_event_stream_id(t(), String.t() | atom()) :: t()
```

Narrows the scope to a specific event stream id. Mirrors .NET's
`ConcurrencyScopeBuilder.WithEventStreamId()`.

# `with_event_stream_type`

```elixir
@spec with_event_stream_type(t(), String.t() | atom()) :: t()
```

Narrows the scope to a specific event stream type. Mirrors .NET's
`ConcurrencyScopeBuilder.WithEventStreamType()`.

# `with_event_type`

```elixir
@spec with_event_type(t(), event_type_module()) :: t()
```

Adds a single event type to the scope's narrowing. Mirrors .NET's
`ConcurrencyScopeBuilder.WithEventType()`.

# `with_event_types`

```elixir
@spec with_event_types(t(), [event_type_module()]) :: t()
```

Adds a list of event types to the scope's narrowing. Mirrors .NET's repeated
`ConcurrencyScopeBuilder.WithEventType()` calls.

# `with_sequence_number`

```elixir
@spec with_sequence_number(t(), non_neg_integer()) :: t()
```

Sets the expected sequence number on a scope. Pipe-able continuation of `new/2`,
`for_event_source/2`, or any other scope - mirrors .NET's
`ConcurrencyScopeBuilder.WithSequenceNumber()`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
