base-domain
Version:
simple module to help build Domain-Driven Design
368 lines (257 loc) • 8.42 kB
text/coffeescript
TYPES = require './types'
Base = require './base'
###*
Base model class of DDD pattern.
the parent "Base" class just simply gives a () method.
BaseModel
Base
base-domain
###
class BaseModel extends Base
: false
###*
key-value pair representing typeName - type
use for definition of for each extender
TYPES
Object
###
: TYPES
###*
key-value pair representing property's name - type of the model
firstName : .STRING
lastName : .STRING
age : .NUMBER
registeredAt : .DATE
team : .MODEL 'team'
hobbies : .MODELS 'hobby'
info : .ANY
see types.coffee for full options.
properties
Object
###
: {}
###
properties to cache, private.
###
: undefined
: undefined
: undefined
: undefined
###*
get key-value pair representing property's name - type info of the model
if prop name is given, returns the info
getPropertyInfo
{String} prop
{Object}
###
: (prop) ->
if not ?
= {}
for _prop, type of
typeInfo = .info(type)
[_prop] = typeInfo
if prop
return [prop]
else
return
###*
get prop name whose type is CREATED_AT
notice: only one prop should be enrolled to CREATED_AT
getPropOfCreatedAt
{String} propName
###
: ->
if is undefined
= null
for prop, type of
if type is .CREATED_AT
= prop
break
return
###*
get prop name whose type is UPDATED_AT
notice: only one prop should be enrolled to UPDATED_AT
getPropOfUpdatedAt
{String} propName
###
: ->
if is undefined
= null
for prop, type of
if type is .UPDATED_AT
= prop
break
return
###*
get list of properties which contains relational model
getModelProps
{Array}
###
: ->
if not ?
= []
for prop, typeInfo of ()
if typeInfo.model?
.push prop
return
###*
create plain object without relational entities
descendants of Entity are removed, but not descendants of BaseModel
descendants of Entity in descendants of BaseModel are removed ( = recursive)
FIXME: this method should not be in "factory"
toPlainObject
{Object} plainObject
###
toPlainObject: ->
propInfoMap = .getPropertyInfo()
facade = ()
plainObject = {}
for own prop, value of @
typeInfo = propInfoMap[prop]
# set non-model properties
if not typeInfo?.model?
plainObject[prop] = value
continue
# strip model if it is descendant of Entity
if typeInfo.model
continue
# strip submodel's relation
if typeInfo.name is 'MODEL'
plainObject[prop] = value.toPlainObject()
else # typeInfo.name is 'MODELS'
plainObject[prop] =
for subData in value
subData.toPlainObject()
return plainObject
###*
synchronize relation columns and relationId columns
{Object} [options]
{Boolean} [options.force]
updateRelationIds
###
updateRelationIds: (options = {})->
for propName in .getModelProps()
typeInfo = .getPropertyInfo(propName)
modelName = typeInfo.model
propValue = @[propName]
# should be subclass of entity
if not modelName
continue
(propName, propValue)
return @
###*
set related model(s)
setRelatedModel
{String} prop property name of the related model
{Entity|Array<Entity>} submodel
{BaseModel} this
###
setRelatedModel: (prop, submodel) ->
(prop, 'setRelatedModel(s)')
typeInfo = .getPropertyInfo(prop)
modelName = typeInfo.model
idPropName = typeInfo.idPropName
# when idProp is set and no submodel given, do nothing
# call "unsetRelatedModel" to unset idProp
if @[idPropName]? and not submodel?
return @
@[prop] = submodel
if typeInfo.name is 'MODEL'
@[idPropName] = submodel?.id
else
@[idPropName] =
if submodel
(sub.id for sub in submodel)
else
[]
return @
###*
alias for setRelatedModel
setRelatedModels
###
setRelatedModels: (prop, submodels) -> (prop, submodels)
###*
unset related model(s)
{String} prop property name of the related models
{BaseModel} this
setRelatedModels
###
unsetRelatedModel: (prop) ->
(prop, 'unsetRelatedModel(s)')
typeInfo = .getPropertyInfo(prop)
modelName = typeInfo.model
idPropName = typeInfo.idPropName
@[prop] = undefined
if typeInfo.name is 'MODEL'
@[idPropName] = undefined
else
@[idPropName] = []
return @
###*
alias for unsetRelatedModel
unsetRelatedModels
###
unsetRelatedModels: (prop, submodels) -> (prop, submodels)
###*
add related models
{String} prop property name of the related models
{BaseModel} this
addRelatedModels
###
addRelatedModels: (prop, submodels...) ->
(prop, 'addRelatedModels')
typeInfo = .getPropertyInfo(prop)
modelName = typeInfo.model
if typeInfo.name isnt 'MODELS'
throw ().error """
#{@constructor.name}.addRelatedModels(#{prop})
#{prop} is not a prop for models.
"""
idPropName = typeInfo.idPropName
@[prop] ?= []
@[prop].push submodel for submodel in submodels
@[idPropName] ?= []
@[idPropName].push submodel.id for submodel in submodels
return @
###*
assert given prop is entity prop
assertEntityProp
###
assertEntityProp: (prop, method) ->
typeInfo = .getPropertyInfo(prop)
if not typeInfo? or not typeInfo.model
throw ().error """
#{@constructor.name}.#{method}(#{prop})
#{prop} is not a prop for model.
"""
modelName = typeInfo.model
# should be subclass of entity
if not modelName
throw ().error """
#{@constructor.name}.#{method}(#{prop})
#{prop} is a prop for model, but not subclass of Entity.
"""
return
###*
return if Model is subclass of Entity
isSubClassOfEntity
###
isSubClassOfEntity: (modelName) ->
ModelClass = ().getModel modelName
return ModelClass.isEntity
module.exports = BaseModel