Domain
Home / Domain
Domain is a DDD model, written in the F# as
.fsx
(F# script).
It should only contain Types
(at least for now).
Table of contents
How does it work?
It uses a Fsharp.Compiler.Service under the hood.
- compiler parses a
.fsx
files - then a Domain Resolver resolves a domain types for Tuc
- there is also a Domain Checker, which checks the basic mistakes and shows types, which are not defined or supported
Domain types
You can use whatever type you want, but abbreviation.
Abbreviation is not supported, since the compiler just replace the Abbreviation with the real type, it doesn’t mean anything in the Domain.
type Id = string // this is abbreviation for string
type Id = Id of string // this is correct
// or use just an Id, if you don't want to specify a concrete type
type Id = Id
type Event = Event // this is ok
type SomethingCreatedEvent = Event // this is abbreviation for Event
type SomethingCreatedEvent = SomethingCreatedEvent of Event // this is correct
Common types
Common types helps a Resolver to resolve a type with specific purpose or usage.
Initiator
Initiator is the main service, which occurs in the use-case.
type MyMainService = Initiator
Data Object
Data object is defined as a list of Data.
It allows you to post data into the data object and read data from data object.
type DataObject<'Data> = DataObject of 'Data list
Data Objects are used as a type of your concrete data object.
For example, when you have a database of Persons, it might be as following:
type Database<'Entity> = Database of 'Entity list
type PersonDatabase = PersonDatabase of Database<Person>
and Person = {
Name: string
}
Stream
Stream is defined as a list of Events. Stream type is one of a specific Data Objects.
It allows you to post events into the stream, read events from stream or handle an event in stream.
type Stream<'Event> = Stream of 'Event list
Stream are used as a type of your concrete stream.
For example, when you have a stream of Interactions, it might be as following:
type InteractionStream = InteractionStream of Stream<InteractionEvent>
and InteractionEvent =
| Confirmed
| Rejected
Handler
Handler is a generic function, which handles a data.
It must be a generic type with exactly one generic parameter and it must have a Handler
suffix.
Handlers allow you to handle a Data from DataObjects, by a special tuc syntax.
type Handler<'Data> = Handler of ('Data -> unit)
So for example you can have a StreamHandler
, which would handle an Event in a Stream.
type StreamHandler<'Event> = StreamHandler of ('Event -> unit)
Handler is just a function, so you need a Service, to contain such handler.
type StreamListenerService = {
OnInteractionEvent = StreamHandler<InteractionEvent>
}
In StreamListenerService
record, there now would be a Handler OnInteractionEvent
, which can handle an InteractionEvent
from a Stream
of such event.
Domain example
// Common types
type Id = UUID
type Stream<'Event> = Stream of 'Event list
type StreamHandler<'Event> = StreamHandler of ('Event -> unit)
// Types
type InteractionEvent =
| Confirmation
| Rejection
type InteractionResult =
| Accepted
| Error
type IdentityMatchingSet = {
Contact: Contact
}
and Contact = {
Email: Email option
Phone: Phone option
}
and Email = Email of string
and Phone = Phone of string
type Person =
| Known of PersonId
| Unknown
and PersonId = PersonId of Id
// Streams
type InteractionCollectorStream = InteractionCollectorStream of Stream<InteractionEvent>
// Services
type GenericService = Initiator
type InteractionCollector = {
PostInteraction: InteractionEvent -> InteractionResult
}
type PersonIdentificationEngine = {
OnInteractionEvent: InteractionEvent -> unit
}
type PersonAggregate = {
IdentifyPerson: IdentityMatchingSet -> Person
}
Share types between domains
There is often a situation, where you need to share some types between multiple domains (DTOs, Common Types, …).
You can use a #load
key word in .fsx
file.
TIP: You should avoid loading one domain from another, since domains should have its boundaries.
// common.fsx
type Id = UUID
type Stream<'Event> = Stream of 'Event list
type StreamHandler<'Event> = StreamHandler of ('Event -> unit)
// cars-persons-shared.fsx
#load "common.fsx"
open Common
type CarDto = {
Id: Id
Name: string
Type: string
}
type PersonDto = {
Id: Id
Name: string
Address: string
}
// carsDomain.fsx
#load "cars-persons-share.fsx"
open ``Cars-persons-share``
open Common
type CarFinder = {
FindCar: Id -> CarDto
}
// personsDomain.fsx
#load "cars-persons-share.fsx"
open ``Cars-persons-share``
open Common
type PersonEvent =
| BoughtCar of PersonBoughtCarEvent
and PersonBoughtCarEvent = {
PersonId: Id
Car: CarDto
}
type PersonStream = PersonStream of Stream<PersonEvent>