Skip to content

Commit

Permalink
V0.3.0 (#1)
Browse files Browse the repository at this point in the history
* add logic to hook

* add gh-pages to dev dependencies

Co-authored-by: nhatnm <[email protected]>
  • Loading branch information
milhlhat and nhatnm authored Jan 20, 2023
1 parent e048a70 commit 7805ba7
Show file tree
Hide file tree
Showing 26 changed files with 373 additions and 169 deletions.
Binary file added CA-flow.drawio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed Clean-architecture-usage.png
Binary file not shown.
151 changes: 99 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,71 +1,118 @@
# Clean Architecture
<img src="./clean-architecture.webp"/>
<img src="./clean-architecture.png" style="max-width: 800px;"/>

- ## Folder style ❤️
## Folder style ❤️
<pre>
.
├── modules
│ ├── transaction
│ │ ├── apdapter
│ │ │ ├── createTransactionAdapter.ts
│ │ │ └── deleteTransactionApdapter.ts
│ │ ├── presentation
│ │ ├── adapters
│ │ │ ├── index.ts
│ │ │ ├── useCreateTransactionAdapter.ts
│ │ │ ├── useDeleteTransactionAdapter.ts
│ │ │ └── useGetAllTransactionAdapter.ts
│ │ ├── domains
│ │ │ ├── transaction.entity.ts
│ │ │ ├── transaction.model.ts
│ │ │ └── transaction.repository.ts
│ │ ├── presentations
│ │ │ ├── TransactionCreate
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.css
│ │ │ │ └── TransactionCreate.tsx
│ │ │ ── TransactionList
│ │ │ ├── index.tsx
│ │ │ ├── style.css
│ │ │ ├── TransactionItem.tsx
│ │ │ └── TransactionList.tsx
│ │ ── use-case
│ │ │ └── spendMoreThanIncomeUseCase.ts
│ │ ├── config.ts
│ │ ├── transaction.entity.ts
│ │ ── transaction.model.ts
│ │ └── transaction.repository.ts
│ │ │ ── TransactionList
│ │ │ ├── index.tsx
│ │ │ ├── style.css
│ │ │ ├── TransactionItem.tsx
│ │ │ └── TransactionList.tsx
│ │ │ └── .DS_Store
│ │ ── use-cases
│ │ ├── createTransactionUseCase.ts
│ │ ├── deleteTransactionUseCase.ts
│ │ │ └── index.ts
│ │ └── helper.ts
│ └── user
├── utils
│ ├── dateUtils.ts
│ ├── dateUtil.ts
│ ├── exceptionUtil.ts
│ └── toastUtil.ts
├── App.test.tsx
├── App.tsx
├── index.css
└── index.tsx

</pre>

## Usage 💪
## Flow 💪
<img src="./CA-flow.drawio.png" style="max-width: 600px;width:100%"/>

<img src="./Clean-architecture-usage.png" style="max-width: 400px;"/>
## Usage 🐾

1. Presentation

export default Feature(){
const {state, handleSomething} = useAdapter()

---
- ## Other Folder style 😌
<pre>
.
├── adapters
├── domain
│ ├── transaction
│ │ ├── transaction.entity.ts
│ │ ├── transaction.model.ts
│ │ └── transaction.repository.ts
│ └── user
├── presentations
│ ├── transaction
│ │ ├── TransactionCreate
│ │ │ ├── style.css
│ │ │ └── TransactionCreate.tsx
│ │ └── TransactionList
│ │ ├── style.css
│ │ ├── TransactionItem.tsx
│ │ └── TransactionList.tsx
│ └── user
├── use-cases
│ ├── transaction
│ │ ├── createTransactionUseCase.ts
│ │ ├── deleteTransactionUseCase.ts
│ │ └── listTransactionUseCase.ts
│ └── user
├── utils
│ ├── exceptionUtil.ts
│ └── toastUtil.ts
├── App.tsx
</pre>
return (
<button onClick={handleSomething}>
{state}
</button>
)
}
2. Adapter

import someUseCase from "../use-cases"
export default useAdapter(service: ServiceRepository){
const [state, setState] = useState();

const handleSomething = ()=> {
const result = someUseCase.execute();
setState(result);
}
return {state, handleSomething}
}

3. Use case

import service from "../domain/model"

export class SomeUseCase {
execute (){
handleSomeUserStories();
service.doSomething();
}
}

4. Domain

4.1 Entity

export interface Something {
id: number;
...
}

4.2 Repository

export interface SomethingRepository {
getSomething(): Something;
setSomething(st: Something): Something;
...
}

4.3 Model

export class SomethingService implements SomethingRepository {
getSomething(){
callApi();
}

setSomething(st: Something){
validate(st)
callApi(st)
}
}
Binary file added clean-architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed clean-architecture.webp
Binary file not shown.
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "clean-architecture",
"version": "0.1.0",
"private": true,
"homepage": "https://milhlhat.github.io/react-clean-architecture",
"dependencies": {
"@formkit/auto-animate": "^1.0.0-beta.5",
"@testing-library/jest-dom": "^5.14.1",
Expand All @@ -23,7 +24,9 @@
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
"eject": "react-scripts eject",
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
},
"eslintConfig": {
"extends": [
Expand All @@ -44,6 +47,7 @@
]
},
"devDependencies": {
"@types/uuid": "^9.0.0"
"@types/uuid": "^9.0.0",
"gh-pages": "^4.0.0"
}
}
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import { TransactionList } from 'modules/transaction/presentation/TransactionList';
import { TransactionList } from 'modules/transaction/presentations/TransactionList';
import { ToastContainer } from 'react-toastify';

function App() {
Expand Down
42 changes: 0 additions & 42 deletions src/modules/transaction/adapters/createTransactionAdapter.ts

This file was deleted.

13 changes: 0 additions & 13 deletions src/modules/transaction/adapters/deleteTransactionAdapter.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/modules/transaction/adapters/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from "./createTransactionAdapter";
export * from "./deleteTransactionAdapter";
export * from "./useCreateTransactionAdapter";
export * from "./useDeleteTransactionAdapter";
44 changes: 44 additions & 0 deletions src/modules/transaction/adapters/useCreateTransactionAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useCallback } from 'react';
import { abortError, notifyError } from 'utils/exceptionUtil';
import { toastSuccess } from 'utils/toastUtil';
import { v4 as uuid } from 'uuid';
import { Transaction } from "../domains/transaction.entity";
import { TransactionRepository } from '../domains/transaction.repository';
import CreateTransactionUseCase from '../use-cases/createTransactionUseCase';

export const useCreateTransactionAdapter = (transactionService: TransactionRepository) => {

const convertFormToTransaction = (event: React.ChangeEvent<HTMLFormElement>): Transaction => {
event.preventDefault();
const formData = new FormData(event.target);
const transactionForm: Transaction = Object.fromEntries(formData.entries()) as unknown as Transaction;
return {
id: uuid(),
amount: Number(transactionForm.amount),
date: transactionForm.date,
description: transactionForm.description,
type: transactionForm.type,
};
}

const createTransactionAdapter = useCallback((event: React.ChangeEvent<HTMLFormElement>): boolean => {
try {
// transform data from third party library or framework (e.g. form, etc.) to domain model
const transaction = convertFormToTransaction(event);
const allTransactions = transactionService.getTransactions();
// execute use case or service
// transform data from domain model to third party library framework or UI (e.g. form, component etc.)
// interact with UI, e.g. show error message, show success message, etc. (not related to state management)

const createTransactionUseCase = new CreateTransactionUseCase(transactionService);
createTransactionUseCase.execute(transaction);
toastSuccess("Thêm thành công");
return true
} catch (error) {
notifyError(error);
throw abortError(error);
}

}, [])
return { createTransactionAdapter }
}
20 changes: 20 additions & 0 deletions src/modules/transaction/adapters/useDeleteTransactionAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useCallback } from "react";
import { abortError, notifyError } from "utils/exceptionUtil";
import { toastSuccess } from "utils/toastUtil";
import { TransactionRepository } from "../domains/transaction.repository";
import DeleteTransactionUseCase from "../use-cases/deleteTransactionUseCase";


export const useDeleteTransactionAdapter = (transactionService: TransactionRepository) => {
const deleteTransactionAdapter =useCallback((id: string): void => {
try {
const deleteTransactionUseCase = new DeleteTransactionUseCase(transactionService);
deleteTransactionUseCase.execute(id);
toastSuccess("Xóa thành công");
} catch (error) {
notifyError(error);
throw abortError(error);
}
}, [])
return { deleteTransactionAdapter }
}
22 changes: 22 additions & 0 deletions src/modules/transaction/adapters/useGetAllTransactionAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useCallback, useEffect, useState } from "react";
import { Transaction } from "../domains/transaction.entity";
import { TransactionRepository } from "../domains/transaction.repository";

export default function useGetTransactionAdapter(transactionRepo: TransactionRepository) {
const [allTransactions, setAllTransactions] = useState<Transaction[]>([]);

const loadTransactions = useCallback(() => {
const transactions = transactionRepo.getTransactions().reverse();
if (transactions) {
setAllTransactions(transactions);
} else {
setAllTransactions([]);
}
}, []);

useEffect(() => {
loadTransactions();
}, [loadTransactions]);

return { allTransactions, loadTransactions }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { abortError } from "utils/exceptionUtil";
import { TRANSACTION_KEY } from "./config";
import { Transaction } from "./transaction.model";
import { TRANSACTION_KEY } from "../helper";
import { Transaction } from "./transaction.entity";
import { TransactionRepository } from "./transaction.repository";

class TransactionService implements TransactionRepository {
Expand All @@ -10,7 +10,7 @@ class TransactionService implements TransactionRepository {
return allTransaction ? JSON.parse(allTransaction) : [];
}

getTransactionById(id: string): Transaction {
getTransactionById(id: Transaction["id"]): Transaction {
const allTransactions = this.getTransactions();
if (!allTransactions || (Array.isArray(allTransactions) && allTransactions.length === 0)) {
throw new Error("No transactions found");
Expand Down Expand Up @@ -68,7 +68,7 @@ class TransactionService implements TransactionRepository {
return transaction;
}

deleteTransaction(id: string): void {
deleteTransaction(id: Transaction["id"]): void {
const allTransactions = this.getTransactions();
const index = allTransactions.findIndex((t: Transaction) => t.id === id);
if (index === -1) {
Expand Down
Loading

0 comments on commit 7805ba7

Please sign in to comment.