Моделирование слоя для базовой системы документооборота


Как до сих пор я, в основном, развитие существующих продуктов и обслуживания/машиностроение чувствую нужно что-то модели время от времени для развлечений, так и для собственных образовательных целей.

Прилагается код-это простая модель предметной области системы документооборота, такие как Jira (я признаю, что был частично под влиянием этой конкретной модели).

Это очень базовая модель, конечно. Он не поддерживает многие понятия, которые я хотел бы видеть в такого рода программного обеспечения, однако, как я уже говорил до этого что-то вроде небольшой тренировки.

Начнем от самого рабочего процесса. Рабочий процесс содержит определение задач (какие поля должны его иметь), возможных состояний и их переходов, а также группам (от задачи).

@Document
data class Workflow(
    val id: String = generateId(),
    val name: String,
    val taskTemplate: TaskTemplate,
    val stateTransitions: StateTransitions = StateTransitions.basic(),
    val rootTaskGroup: TaskGroup = TaskGroup.root())

Это как шаблон задачи выглядит следующим образом:

 data class TaskTemplate(val taskPropertyDefinitions: List<TaskPropertyDefinition> = listOf()) {

    val taskPropertyDefinitionsMap = taskPropertyDefinitions.map { it.name to it }.toMap()

    companion object {
        fun basic(): TaskTemplate {
            return TaskTemplate(listOf(
                    TaskPropertyDefinition("title", StringTaskProperty::class),
                    TaskPropertyDefinition("description", StringTaskProperty::class)
            ))
        }
    }

    fun addPropertyDefinition(taskPropertyDefinition: TaskPropertyDefinition): TaskTemplate {
        return this.copy(taskPropertyDefinitions = this.taskPropertyDefinitions + taskPropertyDefinition)
    }

    fun getTaskPropertyDefinition(propertyDefinitionName: String) = taskPropertyDefinitionsMap[propertyDefinitionName]

    fun propertyIsValid(taskProperty: TaskProperty): Boolean {
        return getTaskPropertyDefinition(taskProperty.propertyName)?.let { definition ->
            taskProperty.matchesDefinition(definition)
        } ?: false
    }
}


data class TaskPropertyDefinition(val name: String,
                                  val propertyType: KClass<out TaskProperty>,
                                  val optional: Boolean = false)

abstract class TaskProperty(val propertyName: String) {

    abstract fun matchesDefinition(taskPropertyDefinition: TaskPropertyDefinition): Boolean

}

data class StringTaskProperty(val name: String, val value: String?) : TaskProperty(name) {

    override fun matchesDefinition(taskPropertyDefinition: TaskPropertyDefinition): Boolean {
        return if (!taskPropertyDefinition.optional) {
            value != null
        } else true
    }
}

data class MultiStringTaskProperty(val name: String, val values: List<String> = listOf()) : TaskProperty(name) {

    override fun matchesDefinition(taskPropertyDefinition: TaskPropertyDefinition): Boolean {
        return if (!taskPropertyDefinition.optional) {
            values.isNotEmpty()
        } else true
    }
}

...и т. д. - там возможно много разных свойств задач, как чек-лист, некоторые выбирает, multiselects.

Переходы состояний:

data class StateTransitions(val initialState: State, val stateTransitions: List<StateTransition>) : Valid {

    override fun isValid(): Boolean {
        return true
    }

    companion object {
        fun basic(): StateTransitions {
            return StateTransitions(State("New"),
                    listOf(StateTransition.oneToOne((State("New")), State("Done"))))
        }
    }

    fun transitionAllowed(sourceState: State, destinationState: State): Boolean {
        stateTransitions.find { it.sourceState == sourceState }?.let { transition ->
            if (transition.allowedDestinations.contains(destinationState)) return true
        }
        return false
    }
}

data class StateTransition(val sourceState: State, val allowedDestinations: List<State>) {
    companion object {
        fun oneToOne(sourceState: State, destinationState: State): StateTransition {
            return StateTransition(sourceState, listOf(destinationState))
        }
    }
}

data class State(val name: String)

Сейчас вещи, связанные с самой задачей:

@Document
data class Task(@Id val id: String = generateId(),
                @DBRef val workflow: Workflow,
                val taskGroups: Set<TaskGroup> = setOf(workflow.rootTaskGroup),
                val taskHistory: TaskHistory = TaskHistory.start(),
                val state: State = workflow.stateTransitions.initialState,
                val taskProperties: Map<String, TaskProperty> = mapOf(),
                val published: Boolean = false) {

    fun applyAction(action: TaskAction): Task {
        return when (action) {
            is AddToGroupsAction -> addToGroupsAction(action)
            is PublishAction -> publishAction(action)
            is StateChangeAction -> stateChangeAction(action)
            is UpdatePropertiesAction -> updatePropertiesAction(action)
            else -> throw UnrecognizedActionException(action, this.id)
        }
    }

    private fun stateChangeAction(action: StateChangeAction): Task {
        return if (workflow.stateTransitions.transitionAllowed(this.state, action.newState))
            this.copy(state = action.newState, taskHistory = this.taskHistory.action(action))
        else throw DisallowedStateTransitionException(state, this.id)
    }

    private fun publishAction(action: PublishAction): Task {
        return this.copy(published = true, taskHistory = this.taskHistory.action(action))
    }

    private fun addToGroupsAction(action: AddToGroupsAction): Task {
        return this.copy(taskGroups = this.taskGroups + action.groups, taskHistory = this.taskHistory.action(action))
    }

    private fun updatePropertiesAction(action: UpdatePropertiesAction): Task {
        return this.copy(taskProperties = updateProperties(action.newProperties, workflow.taskTemplate),
                taskHistory = this.taskHistory.action(action))
    }

    private fun updateProperties(newProperties: List<TaskProperty>, taskTemplate: TaskTemplate): Map<String, TaskProperty> {
        return HashMap(taskProperties).also { properties ->
            newProperties.associateBy {
                it.propertyName
            }.forEach { propertyName, property ->
                if (taskTemplate.propertyIsValid(property)) {
                    properties += propertyName to property
                }
            }
        }
    }
}

data class TaskGroup(val name: String, val childGroups: List<TaskGroup> = listOf()) {

    companion object {
        fun root() = TaskGroup("root")
    }

    fun addChildGroup(taskGroup: TaskGroup): TaskGroup {
        return this.copy(childGroups = this.childGroups + taskGroup)
    }

    fun getChildGroup(wantedGroupName: String): TaskGroup? {
        fun nameMatches(childGroup: TaskGroup, groupName: String) = childGroup.name == groupName
        mutableStackOf(this).let { stack ->
            while (stack.isNotEmpty()) {
                val currentGroup = stack.remove()
                if (nameMatches(currentGroup, wantedGroupName)) return currentGroup
                currentGroup.childGroups.forEach { currentGroupChild ->
                    if (nameMatches(currentGroupChild, wantedGroupName)) return currentGroupChild
                    stack.addAll(currentGroupChild.childGroups)
                }
            }
            return null
        }
    }
}

data class TaskHistory(val taskActions: List<TaskAction> = listOf()) {
    companion object {
        fun start() : TaskHistory {
            return TaskHistory(listOf(CreatedTaskAction))
        }
    }

    fun action(action: TaskAction): TaskHistory {
        return this.copy(taskActions = this.taskActions + action)
    }
}

И действия:

abstract class TaskAction(val actionTime: LocalDateTime = LocalDateTime.now())
class AddToGroupsAction(val groups: List<TaskGroup>) : TaskAction()
object CreatedTaskAction : TaskAction()
object PublishAction : TaskAction()
data class StateChangeAction(val newState: State) : TaskAction()
data class UpdatePropertiesAction(val newProperties: List<TaskProperty>) : TaskAction()

И опять же - многие действия не хватает. Вот я боюсь, что если это будет хорошая идея, чтобы обработать действия с помощью "когда" выражение, когда допустим, у нас есть 25-50 действия для борьбы с.

Я был бы очень признателен за любые предложения или мнения об этой простой модели.



107
1
задан 11 февраля 2018 в 08:02 Источник Поделиться
Комментарии