Skip to content

Commit

Permalink
Clarify message, pin behavior re hyphens in StreamName (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
dharmaturtle authored Nov 9, 2020
1 parent 57e08df commit 2cc95e1
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 5 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,13 @@ The de-facto standard Event Store [EventStore.org](https://eventstore.org) and i

Where:

- `{CategoryName}` represents a high level contract/grouping; all stream names starting with `Category-` are the same category
- `{CategoryName}` represents a high level contract/grouping; all stream names starting with `Category-` are the same category. Must not contain any `-` characters.
- `-` (hyphen/minus) represents the by-convention standard separator between category and identifier
- `{Identifier}` represents the identity of the Aggregate / Aggregate Root instance for which we're storing the events within this stream. It should not embed `-` characters (but see below re usage of `_` to manage a composite id). Note `StreamName` will actively reject such values by throwing exceptions when fields erroneously have embedded `-` or `_` values (see xmldoc on the relevant functions and/or [look at the code](https://github.com/jet/FsCodec/blob/master/src/FsCodec/StreamName.fs)).
- `{Identifier}` represents the identity of the Aggregate / Aggregate Root instance for which we're storing the events within this stream. The `_` character is used to separate composite ids; see [the code](https://github.com/jet/FsCodec/blob/master/src/FsCodec/StreamName.fs).

Its important to note that composing a stream name directly from values in the Domain that might include characters such as `-` (which by default drives `$ec` indexing in EventStoreDB) and/or arbitrary Unicode chars (which may not work well for other backing stores e.g. if CosmosDB were to restrict the character set that may be used for a Partition Key) is not a good idea in general (you'll also want to ensure its appropriately cleansed, validated and/or canonicalized to cover SQL Injection and/or XSS concerns). In short, no, you shouldn't just stuff an email address into the `{Identifier}` portion.
The `StreamName` module will reject invalid values by throwing exceptions when fields have erroneously embedded `-` or `_` values.

It's important to apply some consideration in mapping from values in your domain to a `StreamName`. Domain values might include characters such as `-` (which may cause issues with EventStoreDb's [`$by_category`](https://developers.eventstore.com/server/5.0.8/server/projections/system-projections.html#by-category) projections) and/or arbitrary Unicode chars (which may not work well for other backing stores e.g. if CosmosDB were to restrict the character set that may be used for a Partition Key). You'll also want to ensure it's appropriately cleansed, validated and/or canonicalized to cover SQL Injection and/or XSS concerns. In short, no, you shouldn't just stuff an email address into the `{Identifier}` portion.

[`FsCodec.StreamName`](https://github.com/jet/FsCodec/blob/master/src/FsCodec/StreamName.fs): presents the following set of helpers that are useful for splitting and filtering Stream Names by Categories and/or Identifiers. Similar helpers would of course make sense in other languages e.g. C#:

Expand Down
4 changes: 2 additions & 2 deletions src/FsCodec/StreamName.fs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module StreamName =
/// category is separated from id by `-`
let create (category : string) aggregateId : StreamName =
if category.IndexOf '-' <> -1 then invalidArg "category" "may not contain embedded '-' symbols"
UMX.tag<streamName> (sprintf "%s-%s" category aggregateId)
UMX.tag (sprintf "%s-%s" category aggregateId)

/// Composes a StreamName from a category and > 1 name elements.
/// category is separated from the aggregateId by '-'; elements are separated from each other by '_'
Expand All @@ -44,7 +44,7 @@ module StreamName =
/// Throws <code>InvalidArgumentException</code> if it does not adhere to that form
let parse (rawStreamName : string) : StreamName =
if rawStreamName.IndexOf('-') = -1 then
invalidArg (sprintf "Stream Name '%s' did not contain exactly one '-' separator" rawStreamName) "streamName"
invalidArg (sprintf "Stream Name '%s' must contain a '-' separator" rawStreamName) "streamName"
UMX.tag rawStreamName

(* Parsing: Raw Stream name Validation functions/pattern that handle malformed cases without throwing *)
Expand Down
5 changes: 5 additions & 0 deletions tests/FsCodec.Tests/StreamNameTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ let [<Fact>] ``Can roundtrip single aggregateIds with embedded dashes and unders

test <@ let (StreamName.CategoryAndIds (scat, aggIds)) = sn
scat = cat && ["a-b";"c-d"] = List.ofArray aggIds @>

let [<Fact>] ``StreamName.parse throws given 0 separators`` () =
raisesWith<System.ArgumentException>
<@ StreamName.parse "Cat" @>
(fun e -> <@ e.Message = "streamName (Parameter 'Stream Name 'Cat' must contain a '-' separator')" @>)

0 comments on commit 2cc95e1

Please sign in to comment.