Searching We.Love.Privacy.Club

Twts matching #generative
Sort by: Newest, Oldest, Most Relevant
In-reply-to » Progress! so i have moved into working on aggregates. Which are a grouping of events that replayed on an object set the current state of the object. I came up with this little bit of generic wonder.

(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 Objects

A 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
}

⤋ Read More
In-reply-to » Hi, I am playing with making an event sourcing database. Its super alpha but I thought I would share since others are talking about databases and such.

Progress! so i have moved into working on aggregates. Which are a grouping of events that replayed on an object set the current state of the object. I came up with this little bit of generic wonder.

type PA[T any] interface {
	event.Aggregate
	*T
}

// Create uses fn to create a new aggregate and store in db.
func Create[A any, T PA[A]](ctx context.Context, es *EventStore, streamID string, fn func(context.Context, T) error) (agg T, err error) {
	ctx, span := logz.Span(ctx)
	defer span.End()

	agg = new(A)
	agg.SetStreamID(streamID)

	if err = es.Load(ctx, agg); err != nil {
		return
	}

	if err = event.NotExists(agg); err != nil {
		return
	}

	if err = fn(ctx, agg); err != nil {
		return
	}

	var i uint64
	if i, err = es.Save(ctx, agg); err != nil {
		return
	}

	span.AddEvent(fmt.Sprint("wrote events = ", i))

	return
}

fig. 1

This lets me do something like this:

a, err := es.Create(ctx, r.es, streamID, func(ctx context.Context, agg *domain.SaltyUser) error {
		return agg.OnUserRegister(nick, key)
})

fig. 2

I can tell the function the type being modified and returned using the function argument that is passed in. pretty cray cray.

⤋ Read More

Planning next to your code – GitHub Projects is now generally available
Today, we are announcing the general availability of the new and improved Projects powered by GitHub Issues. GitHub Projects connects your planning directly to the work your teams are doing in GitHub and flexibly adapts to whatever your team needs at any point. ⌘ Read more

⤋ Read More

**Ten years ago, the 3CD compilation “Bearded Snails” was released, featuring 16 tracks and 19 artists of the “A Beard of Snails Records” roster.

The last of those tracks is kokori’s “Attention”, in its first release to the general public. Now here: https://the20th.bandcamp.com/track/attention**
Ten years ago, the 3CD compilation “Bearded Snails” was released, featuring 16 tracks and 19 artists of the “A Beard of Snails Records” roster.

The last of those tracks is kokori’s “Attention”, in its first release to … ⌘ Read more

⤋ Read More

GitHub Copilot is generally available to all developers
We’re making GitHub Copilot, an AI pair programmer that suggests code in your editor, generally available to all developers for $10 USD/month or $100 USD/year. It will also be free to use for verified students and maintainers of popular open source projects. ⌘ Read more

⤋ Read More

GitHub Enterprise Server 3.5 is now generally available
GitHub Enterprise Server 3.5 is available now, including access to the Container registry, the addition of Dependabot, enhanced administrator capabilities, and features for GitHub Advanced Security. ⌘ Read more

⤋ Read More

I’ve noticed a gradient of skepticism about climate change, ranging from outright contradiction that it exists to debates about the specific measures needed to reverse acknowledged warming trends. Using the general term “climate change denial” to describe that entire gradient, and thus treating all forms of skepticism as equally ignorant, seems counterproductive to me.

⤋ Read More

GitHub Enterprise Server 3.4 improves developer productivity and adds reusable workflows to CI/CD
The GitHub Enterprise Server 3.4 release candidate delivers enhancements to make life easier and more productive, from keyboard shortcuts to auto-generated release notes! ⌘ Read more

⤋ Read More
In-reply-to » Spent the weekend with the state Democrats at our platform convention. Good work and glad to have participated, but 20 hours of zoom over 60 hours is a lot of zoom.

Agreed on “aggressive” — as a general rule, I don’t think most folks are acting like we’re in the kind of crisis we are.

⤋ Read More

Apple’s event on Monday is bringing, as always, speculation to the table. One thing most outlets seem to agree is the introduction of an “M1X” chip, thought Apple might call it differently. M1X might also mean, M1(we don’t know what comes after, or next generation). Either way, I would really like to see the return of the 27” iMac, but I will not hold my breath. Nevertheless, Monday is going to be an exciting day for many, including me! 🍎

⤋ Read More

GitHub security update: revoking weakly-generated SSH keys
On September 28, 2021, we received notice from the developer Axosoft regarding a vulnerability in a dependency of their popular git GUI client - GitKraken. An underlying issue with a dependency, called `keypair`, resulted in the GitKraken client generating weak SSH keys. ⌘ Read more

⤋ Read More

A new public beta of GitHub Releases: How we’re improving the release experience
GitHub Releases has a new look and updated tools to make it easier for open source communities to create and share high-quality releases with auto-generated release notes. ⌘ Read more

⤋ Read More

new algo in #sndkit @!(sndkitref “sparse”)!@ is a sparse noise generator, similar to velvet noise, that produces a random series of impulses at a specified rate in Hz.

⤋ Read More

GitHub Enterprise Server 3.2 is now generally available
Today, we’re excited to announce that GitHub Enterprise Server 3.2 is generally available. This release brings over 70 new features and changes that improve developer experience and deliver new security capabilities. ⌘ Read more

⤋ Read More

What’s new from GitHub Changelog? August 2021 Recap
What did we ship in August? Codespaces, Discussions, and lots of other updates, from the general availability of the dark high contrast theme to an auto-generated table of contents for wikis. ⌘ Read more

⤋ Read More

new algorithm in @!(ref “sndkit”)!@: @!(sndkitref “phasor”)!@ generates a normalized periodic ramp signal, typically used for table-lookup oscillators.

⤋ Read More

with some scripting, I could probably use my upcoming !weewiki !zettelkasten as a drop-in replacement for !twtxt, and then generate the twtxt file. however, I think I am going to keep them separate for the time being. let them both grow to serve different purposes.

⤋ Read More