// Save appends all events in the event stream to the database. func (s *EventStore) Save(events []eh.Event, originalVersion int) error { if len(events) == 0 { return eh.ErrNoEventsToAppend } // Build all event records, with incrementing versions starting from the // original aggregate version. eventRecords := make([]*eventRecord, len(events)) aggregateID := events[0].AggregateID() for i, event := range events { // Only accept events belonging to the same aggregate. if event.AggregateID() != aggregateID { return ErrInvalidEvent } // Marshal event payload. payload, err := dynamodbattribute.MarshalMap(event) if err != nil { // return ErrCouldNotMarshalEvent return err } // Create the event record with current version and timestamp. eventRecords[i] = &eventRecord{ AggregateID: event.AggregateID().String(), Version: 1 + originalVersion + i, Timestamp: time.Now(), EventType: event.EventType(), Payload: payload, } } // TODO: Implement atomic version counter for the aggregate. // TODO: Batch write all events. for _, record := range eventRecords { // Marshal and store the event record. item, err := dynamodbattribute.MarshalMap(record) if err != nil { return err } putParams := &dynamodb.PutItemInput{ TableName: aws.String(s.config.Table), ConditionExpression: aws.String("attribute_not_exists(AggregateID) AND attribute_not_exists(Version)"), Item: item, } if _, err = s.service.PutItem(putParams); err != nil { if err, ok := err.(awserr.RequestFailure); ok && err.Code() == "ConditionalCheckFailedException" { return ErrCouldNotSaveAggregate } return err } } return nil }
// SaveUser creates or updates a user. Upon creation, users are assigned a unique ID. func (db *DynamoDB) SaveUser(u *models.User) error { if u.ID == "" { id, err := shortid.ID(64) if err != nil { return err } u.ID = id } item, err := dynamodbattribute.MarshalMap(u) if err != nil { return err } _, err = db.DB.PutItem(&dynamodb.PutItemInput{ TableName: aws.String("users"), Item: item, }) if err != nil { return err } return nil }