eventric
Version:
behavior-first application development
104 lines (76 loc) • 3.58 kB
text/coffeescript
Aggregate = require 'eventric/aggregate'
uuidGenerator = require 'eventric/uuid_generator'
domainEventService = require 'eventric/domain_event/domain_event_service'
class AggregateRepository
constructor: (params) ->
@_logger = require('eventric').getLogger()
@_aggregateName = params.aggregateName
@_AggregateClass = params.AggregateClass
@_context = params.context
@_store = @_context.getDomainEventsStore()
load: (aggregateId) =>
new Promise (resolve, reject) =>
@_store.findDomainEventsByAggregateId aggregateId, (error, domainEvents) =>
if error
reject error
return
if not domainEvents?.length
reject new Error "No domainEvents for aggregate of type #{@_aggregateName} with #{aggregateId} available"
return
domainEvents = domainEventService.sortDomainEventsById domainEvents
aggregateDeletedEvent = null
for domainEvent in domainEvents by -1
if domainEvent.name.indexOf("#{@_aggregateName}Deleted") > -1
aggregateDeletedEvent = domainEvent
break
if aggregateDeletedEvent?
reject new Error "Aggregate of type #{@_aggregateName} with id #{aggregateId} is marked as deleted because of \
domain event #{aggregateDeletedEvent.name} with domain event id #{aggregateDeletedEvent.id}"
return
aggregate = new Aggregate @_context, @_aggregateName, @_AggregateClass
aggregate.setId aggregateId
aggregate.applyDomainEvents domainEvents
@_installSaveFunctionOnAggregateInstance aggregate
resolve aggregate.instance
# TODO: Rename to "new" and remove automagic call of create() function on aggregate
create: (params, id = null) =>
Promise.resolve().then =>
aggregate = new Aggregate @_context, @_aggregateName, @_AggregateClass
if typeof aggregate.instance.create isnt 'function'
throw new Error 'No create function on aggregate'
if id?
aggregate.setId id
else
aggregate.setId uuidGenerator.generateUuid()
@_installSaveFunctionOnAggregateInstance aggregate
Promise.resolve aggregate.instance.create params
.then ->
return aggregate.instance
_installSaveFunctionOnAggregateInstance: (aggregate) ->
aggregate.instance.$save = =>
@save aggregate
save: (aggregate) =>
Promise.resolve()
.then =>
if not aggregate
throw new Error "Tried to save unknown aggregate #{@_aggregateName}"
domainEvents = aggregate.getNewDomainEvents()
if not domainEvents?.length
throw new Error "No new domain events to save for aggregate of type #{@_aggregateName} with id #{aggregate.id}"
@_logger.debug "Going to Save and Publish #{domainEvents.length} DomainEvents from Aggregate #{@_aggregateName}"
# TODO: Think about how to achieve "transactions" to guarantee consistency when saving multiple events
saveDomainEventQueue = Promise.resolve()
savedDomainEvents = []
domainEvents.forEach (domainEvent) =>
saveDomainEventQueue = saveDomainEventQueue.then =>
@_store.saveDomainEvent domainEvent
.then (domainEvent) ->
savedDomainEvents.push domainEvent
saveDomainEventQueue
.then =>
savedDomainEvents.forEach (domainEvent) =>
@_context.getEventBus().publishDomainEvent domainEvent
.catch (error) =>
@_logger.error error, '\n', error.stack
return aggregate.id
module.exports = AggregateRepository