I just resurrected a laptop that had been off for about six months and it needs 585 package updates.
(cont.)
Just to give some context on some of the components around the code structure.. I wrote this up around an earlier version of aggregate code. This generic bit simplifies things by removing the need of the Crud functions for each aggregate.
Domain ObjectsA domain object can be used as an aggregate by adding the event.AggregateRoot
struct and finish implementing event.Aggregate. The AggregateRoot implements logic for adding events after they are either Raised by a command or Appended by the eventstore Load or service ApplyFn methods. It also tracks the uncommitted events that are saved using the eventstore Save method.
type User struct {
Identity string ```json:"identity"`
CreatedAt time.Time
event.AggregateRoot
}
// StreamID for the aggregate when stored or loaded from ES.
func (a *User) StreamID() string {
return "user-" + a.Identity
}
// ApplyEvent to the aggregate state.
func (a *User) ApplyEvent(lis ...event.Event) {
for _, e := range lis {
switch e := e.(type) {
case *UserCreated:
a.Identity = e.Identity
a.CreatedAt = e.EventMeta().CreatedDate
/* ... */
}
}
}
Events
Events are applied to the aggregate. They are defined by adding the event.Meta
and implementing the getter/setters for event.Event
type UserCreated struct {
eventMeta event.Meta
Identity string
}
func (c *UserCreated) EventMeta() (m event.Meta) {
if c != nil {
m = c.eventMeta
}
return m
}
func (c *UserCreated) SetEventMeta(m event.Meta) {
if c != nil {
c.eventMeta = m
}
}
Reading Events from EventStore
With a domain object that implements the event.Aggregate
the event store client can load events and apply them using the Load(ctx, agg)
method.
// GetUser populates an user from event store.
func (rw *User) GetUser(ctx context.Context, userID string) (*domain.User, error) {
user := &domain.User{Identity: userID}
err := rw.es.Load(ctx, user)
if err != nil {
if err != nil {
if errors.Is(err, eventstore.ErrStreamNotFound) {
return user, ErrNotFound
}
return user, err
}
return nil, err
}
return user, err
}
OnX Commands
An OnX command will validate the state of the domain object can have the command performed on it. If it can be applied it raises the event using event.Raise() Otherwise it returns an error.
// OnCreate raises an UserCreated event to create the user.
// Note: The handler will check that the user does not already exsist.
func (a *User) OnCreate(identity string) error {
event.Raise(a, &UserCreated{Identity: identity})
return nil
}
// OnScored will attempt to score a task.
// If the task is not in a Created state it will fail.
func (a *Task) OnScored(taskID string, score int64, attributes Attributes) error {
if a.State != TaskStateCreated {
return fmt.Errorf("task expected created, got %s", a.State)
}
event.Raise(a, &TaskScored{TaskID: taskID, Attributes: attributes, Score: score})
return nil
}
Crud Operations for OnX Commands
The following functions in the aggregate service can be used to perform creation and updating of aggregates. The Update function will ensure the aggregate exists, where the Create is intended for non-existent aggregates. These can probably be combined into one function.
// Create is used when the stream does not yet exist.
func (rw *User) Create(
ctx context.Context,
identity string,
fn func(*domain.User) error,
) (*domain.User, error) {
session, err := rw.GetUser(ctx, identity)
if err != nil && !errors.Is(err, ErrNotFound) {
return nil, err
}
if err = fn(session); err != nil {
return nil, err
}
_, err = rw.es.Save(ctx, session)
return session, err
}
// Update is used when the stream already exists.
func (rw *User) Update(
ctx context.Context,
identity string,
fn func(*domain.User) error,
) (*domain.User, error) {
session, err := rw.GetUser(ctx, identity)
if err != nil {
return nil, err
}
if err = fn(session); err != nil {
return nil, err
}
_, err = rw.es.Save(ctx, session)
return session, err
}
I have updated my eventDB to have subscriptions! It now has websockets like msgbus. I have also added a in memory store that can be used along side the disk backed wal.
I will update tomorrow
One year ago to the date I made the lastest update for #phpub2twtxt to github and now 365 days later I have published #pixelblog as its successor - lets see where things are going for trip around the sun
Okay, I’ve updated my examples page and shared the more complex ssh example. http://a.9srv.net/shortcuts
December 25th Launch
⌘ Read more
Love the new icons on the latest update!
@movq@www.uninformativ.de
Updated. Will it be possible for the subject be moved at the begining instead (like Yarn and tt do)?
@movq@www.uninformativ.de
Meanwhile I only restart my iPhone when an iOS update is available, which normally happens every 4-5 months or so, or more. 😋
@prologic@twtxt.net finally updated yarnd.. FORK!? Awesome!
Once a day.. though if it hasn’t updated in n-months maybe once a week?
updated gemini://sunshinegardens.org/~xj9/wiki/metric-day/ and sigils.sed
Twtxt stopped functioning when tilde club updated their operatin system. It’s no use. I am deleting it.
Plan 9 got three slots in GSoC; we’ll be working on an updated Japanese input method, updating edwood, and getting Oauth2 support. 🎉🐇
@mckinley@twtxt.net @prologic@twtxt.net I have updated the ticket with my findings.. its not what you expect! /clickbait https://github.com/jointwt/twtxt/issues/424
tomo is updated and NO LONGER GPL gemini://sunshinegardens.org/~xj9/posts/2021-05-05-tomo-inferno-license/
maybe i should update my kernel then i wanted to stick to lts, but that may not be an option. does alpine still have a hardened kernel?
Pushed small updates to tw. Next: make discoverability optional.
@vain@www.uninformativ.dedd @lyse@lyse.isobeef.orgdd @prologic@twtxt.netdd Nope.. i have updated my gist to include the feeds listing. feeds.txt
@prologic@twtxt.net the add function just scans recursivley everything.. but the idea is to just add and any new mentions then have a cron to update all known feeds
@prologic@twtxt.net yeah I do.
It seems a bit wonky that it imports from your packages in some places. I’m guessing that’s some legacy bits that need updates?
@lyxal@twtxt.net @prologic@twtxt.net if we edit the txt file does it update on web?
Did some work on WKD handling. Can update keys with HKP posts :) Ugh need to work on docs and unit tests. Boooorrring.
@prologic@twtxt.net Oh snap. that’s what i get for copy paste! ill just have to repost and update my key.
New post on my Gopher site. Back to updating it once a month.
Updated my daily journal at gopher://gopher.johanbove.info:70/notes