テックタッチ株式会社
フロントエンドエンジニア/テックリード
体が固すぎて25才にして腰痛持ち
最近は顎がよく外れる
5年先も通用するソフトウェアを作りたい
より少ない人員でスピードを維持しながらの
機能追加・仕様変更に耐える
チーム全員で理解しやすいレイヤー構造
特定フレームワークに縛られたくない
超シンプルなTODOアプリ
Interface
Adapters
Frameworks
& Drivers
Usecases
Entities
UI (Vue)
Store(Vuex)
Controller
Domain
Interactor
Domain
Storage
API
api
repository
Interface
Adapters
Frameworks
& Drivers
Usecases
Entities
UI (Vue)
Store(Vuex)
Controller
Domain
Interactor
Domain
Storage
API
api
repository
class getters extends Getters<state> {
get tasks() {
return this.state.tasks
}
}
<script lang="ts">
import Vue from 'vue'
import Task from '../entities/Task'
import { controllersMapper } from '@/store/modules/controllers'
import { taskMapper } from '@/store/modules/domain/task'
export default Vue.extend({
name: 'Todo',
data: (): { text: string } => ({
text: ''
}),
computed: taskMapper.mapGetters(['tasks']),
methods: {
...controllersMapper.mapActions(['addTask', 'complete']),
add() {
this.addTask(this.text)
this.text = ''
},
close(index: number) {
this.complete(index)
},
isCompleted(task: Task) {
return task.state !== 'DONE'
}
}
})
</script>
class actions extends Actions<state, getters, mutations> {
taskInteractor!: TaskInteractor
$init(store: Store<any>) {
this.taskInteractor = new TaskInteractor(task.context(store))
}
addTask(title: string) {
if (!title) {
return
}
const task: Task = {
title,
description: '',
state: 'TODO'
}
this.taskInteractor.addTask(task)
}
complete(index: number) {
this.taskInteractor.complete(index)
}
}
export class TaskInteractor {
store: TaskStoreModule
constructor(store: TaskStoreModule) {
this.store = store
}
addTask(task: Task) {
// store
this.store.actions.add(task)
}
complete(index: number) {
const task = this.store.state.tasks[index]
this.store.actions.update(
{
index,
task: setState(task, 'DONE')
}
)
}
}
class actions extends Actions<state, getters, mutations> {
taskInteractor!: TaskInteractor
$init(store: Store<any>) {
// ControllerにTaskInteractorをInject
this.taskInteractor = new TaskInteractor(task.context(store))
}
addTask(title: string) {
if (!title) {
return
}
const task: Task = {
title,
description: '',
state: 'TODO'
}
this.taskInteractor.addTask(task)
}
complete(index: number) {
this.taskInteractor.complete(index)
}
}
export const controllers = new Module({
actions
})
export const controllersMapper = createMapper(controllers
export interface TaskStoreState {
tasks: Task[]
}
export interface TaskStoreActions {
add: (task: Task) => void
update: ({ index, task }: { index: number; task: Task }) => void
}
interface TaskStoreModule {
state: TaskStoreState
actions: TaskStoreActions
}
export class TaskInteractor {
store: TaskStoreModule // storeの抽象に依存
constructor(store: TaskStoreModule) {
this.store = store
}
addTask(task: Task) {
// store
this.store.actions.add(task)
// repository
// this.repository.save(tasks)
}
complete(index: number) {}
}
// vuex-smart-moduleを使用
class state implements TaskStoreState {
tasks: Task[] = []
}
class getters extends Getters<state> {
get tasks() {
return this.state.tasks
}
}
class mutations extends Mutations<state> {
updateTasks(tasks: Task[]) {
this.state.tasks = tasks
}
updateTask({ index, task }: { index: number; task: Task }) {
this.state.tasks.splice(index, 1, task)
}
}
class actions extends Actions<state, getters, mutations>
implements TaskStoreActions {
add(task: Task) {
this.commit('updateTasks', this.state.tasks.concat(task))
}
update({ index, task }: { index: number; task: Task }) {
this.commit('updateTask', {
index,
task
})
}
}
export const task = new Module({
state,
mutations,
actions,
getters
})
export const taskMapper = createMapper(task)
export default interface Task {
title: string
description: string
state: TaskState
}
export function setState(task: Task, taskState: TaskState): Task {
return {
...task,
...{ state: taskState }
}
}
// titleやdescriptionの文字数制限などのvalidation
export class TaskInteractor {
store: TaskStoreModule
constructor(store: TaskStoreModule) {
this.store = store
}
addTask(task: Task) {
// store
this.store.actions.add(task)
// repository
// this.repository.save(tasks)
// api
// this.api.save(tasks)
}
complete(index: number) {
const task = this.store.state.tasks[index]
this.store.actions.update(
{
index,
task: setState(task, 'DONE')
}
)
}
}
Interface
Adapters
Frameworks
& Drivers
Usecases
Entities
UI (Vue)
Store(Vuex)
Controller
Domain
Interactor
Domain
Storage
API
api
repository