Skip to content

Refactor: Reorganize data access with simplified clean architecture#39

Open
Copilot wants to merge 4 commits into
mainfrom
copilot/refactor-data-access-organization
Open

Refactor: Reorganize data access with simplified clean architecture#39
Copilot wants to merge 4 commits into
mainfrom
copilot/refactor-data-access-organization

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 12, 2026

Storage files were scattered across lib/ with React Query exposed directly to components. This reorganizes data access into a three-layer architecture with a simplified, unified API.

Structure

lib/data/
├── infrastructure/      # GitHub API, localStorage persistence
├── repositories/        # Generic QuarterFileRepository for all file types
└── context/            # Unified DataProvider with React Query encapsulated

API Changes

Before:

// Components used multiple hooks
const { activeStorage } = useStorage()
const { editingCount, commitChanges } = useEditingState()
const { content, isPending } = useStorageData(quarterId, "invoices")
const { quarters } = useStorageQuarters()

After:

// Single unified hook with simplified API
const { 
  companyName,
  editingCount, 
  commitChanges,
  getFile,
  isDirtyFile,
  updateFile,
  quarters 
} = useData()

const { content, isPending } = getFile(quarterId, "invoices")
const isEditing = isDirtyFile(quarterId, "invoices")
updateFile(quarterId, "invoices", newData)

Key Improvements

  • Unified getFile(): Single method replaces loadInvoices(), loadExpenses(), loadCashflow() using TypeScript generics
  • Better encapsulation: isDirtyFile() hides internal editing state, updateFile() replaces setEditingFile()
  • Storage abstraction: Components get companyName and getFileUrl() from context, no need to pass storage props
  • Generic repository: Single QuarterFileRepository<T> replaces 3 duplicate repository classes
  • Cleaner error handling: globalError replaces commitError for broader usage

Implementation

  • Repository: Generic QuarterFileRepository<T> handles all file types (invoices, expenses, cashflow) with type safety
  • Context: DataProvider combines storage selection, editing state, and data fetching with React Query fully encapsulated
  • Infrastructure: GitHub API client and localStorage persistence separated from business logic

Files

Removed (11): storage-context.tsx, editing-state-context.tsx, use-storage-data.ts, use-storage-quarters.ts, github-data.ts, github-storage.ts, storage-persistence.ts, storage-types.ts, invoices-repository.ts, expenses-repository.ts, cashflow-repository.ts

Added (6): Architecture doc with Mermaid diagram, unified context, generic repository, 3 infrastructure modules, clean export interface

Updated (12 components): All migrated to simplified useData() API

Architecture documentation with persistence flow diagrams in lib/data/DATA_ARCHITECTURE.md.

Original prompt

This section details on the original issue you should resolve

<issue_title>Refactor: data access better organized</issue_title>
<issue_description>There are many storage files in the lib/ folder, we should move to a different folder and reorganize the code for a clean arquitecture.

Request a Mermaid diagram to better understand dependencies.
Extract data access files that are independent from our business, and then another layer with methods specific to expenses, invoices...</issue_description>

<agent_instructions>Analize this problem carefully and propose a better implementation.
One goal is that we get rid of useQuery, and encapsulate both in memory and remote files in a common context using a clean arquitecture and design of modules and React context and modules. Include a mermaid diagram in a new README file that explains the persistence based in github.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits February 12, 2026 23:25
…tation

Co-authored-by: pablonete <579705+pablonete@users.noreply.github.com>
Co-authored-by: pablonete <579705+pablonete@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor data access for better organization and architecture Refactor: Reorganize data access into clean architecture layers Feb 12, 2026
Copilot AI requested a review from pablonete February 12, 2026 23:30
@pablonete pablonete marked this pull request as ready for review February 17, 2026 18:37
Copy link
Copy Markdown
Owner

@pablonete pablonete left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Iterate and clean up unneeded complexity. It's OK if you postpone some ideas in order to keep this PR small-ish.

Comment thread app/(main)/layout.tsx Outdated
Comment thread components/cashflow-view.tsx Outdated
Comment thread components/expenses-view.tsx Outdated
Comment thread components/expenses-view.tsx Outdated
Comment thread components/attachment-cell.tsx Outdated
Comment thread components/expenses-view.tsx Outdated
Comment thread lib/data/repositories/cashflow-repository.ts Outdated

/**
* Save cashflow for a specific quarter
* Note: Actual saving is coordinated through the DataContext's commit mechanism
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main value the repository class should offer is keeping track of loaded files, and replace them with "dirty versions" until they are saved in a commit.
So the save should not exist here as "commit changes" but there should be an update method that "saves to memory" the file content, which then is declared as dirty, and that later is read by the actual saving logic to "commit changes".

/**
* Load cashflow for a specific quarter
*/
async load(quarterId: string): Promise<FileResult<CashflowFileData>> {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my previous comment, load should not always be "fetching" but sometimes return the content from memory if it was already loaded or it was created in memory. Let's rename accordingly to getContent, and then the complementary method may be updateContent.

- Combine 3 repository classes into single QuarterFileRepository with generics
- Rename commitError -> globalError for broader usage
- Replace loadInvoices/loadExpenses/loadCashflow with unified getFile(quarterId, type)
- Replace getEditingFile with isDirtyFile() for cleaner API
- Rename setEditingFile -> updateFile
- Add companyName to useData() to avoid exposing activeStorage
- Add getFileUrl() to useData() scoped to current storage
- Update all components to use new simplified API

Co-authored-by: pablonete <579705+pablonete@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 18, 2026

@copilot Iterate and clean up unneeded complexity. It's OK if you postpone some ideas in order to keep this PR small-ish.

Done in e066314. Simplified the API significantly:

  • Combined 3 repository classes into 1 generic class
  • Unified data loading with single getFile() method
  • Cleaner naming: isDirtyFile, updateFile, globalError
  • Better encapsulation: storage details hidden from components

Regarding comments about repository tracking loaded/dirty files (2821135877, 2821141717): The current implementation already handles this in DataContext. The repository's role is just to fetch from GitHub - the context tracks which files are loaded vs dirty and returns the appropriate version. This keeps repositories simple and focused. Let me know if you'd like me to restructure this differently.

Copilot AI changed the title Refactor: Reorganize data access into clean architecture layers Refactor: Reorganize data access with simplified clean architecture Feb 18, 2026
Copilot AI requested a review from pablonete February 18, 2026 09:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor: data access better organized

2 participants