diff --git a/libpacstall/cache/__init__.py b/libpacstall/cache/__init__.py
new file mode 100644
index 0000000..1190a86
--- /dev/null
+++ b/libpacstall/cache/__init__.py
@@ -0,0 +1,226 @@
+# __ _ __ ____ __ ____
+# / / (_) /_ / __ \____ ___________/ /_____ _/ / /
+# / / / / __ \/ /_/ / __ `/ ___/ ___/ __/ __ `/ / /
+# / /___/ / /_/ / ____/ /_/ / /__(__ ) /_/ /_/ / / /
+# /_____/_/_.___/_/ \__,_/\___/____/\__/\__,_/_/_/
+#
+# Copyright (C) 2022-present
+#
+# This file is part of LibPacstall.
+#
+# LibPacstall is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# LibPacstall is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# LibPacstall. If not, see .
+
+"""Caching system."""
+
+from datetime import datetime
+from enum import Enum, auto
+from typing import Dict, List, Optional
+
+from sqlalchemy.types import JSON
+from sqlalchemy.types import Enum as SQLAlchemyEnum
+from sqlmodel import Column, Field, Relationship, SQLModel
+
+
+class InstalledStatus(Enum):
+ """
+ Status of an installed package.
+
+ Attributes
+ ----------
+ DIRECT
+ The dependency was directly installed by the user.
+ INDIRECT
+ The dependency was installed by another dependency.
+ """
+
+ DIRECT = auto()
+ INDIRECT = auto()
+
+
+class DependencyType(Enum):
+ """
+ The type of a dependency.
+
+ Attributes
+ ----------
+ REQUIRED
+ The dependency is by the pacscript.
+ OPTIONAL
+ The dependency is not required by the pacscript. It's an optional
+ dependency.
+ """
+
+ REQUIRED = auto()
+ OPTIONAL = auto()
+
+
+class APTDependencyPacscriptLink(SQLModel, table=True):
+ """
+ Link between an APT dependency and a pacscript.
+
+ Attributes
+ ----------
+ dependency_name
+ The name of the dependency.
+ pacscript_name
+ The name of the pacscript.
+ """
+
+ dependency_name: Optional[int] = Field(
+ default=None, foreign_key="aptdependency.name", primary_key=True
+ )
+ pacscript_name: Optional[str] = Field(
+ default=None, foreign_key="pacscript.name", primary_key=True
+ )
+
+
+class APTDependency(SQLModel, table=True):
+ """
+ SQLModel of an APT dependency for a pacscript.
+
+ Attributes
+ ----------
+ name
+ Name of the dependency.
+ dependents
+ List of pacscripts that depend on this dependency.
+ type
+ Type of the dependency.
+ """
+
+ name: str = Field(primary_key=True)
+ dependents: List["Pacscript"] = Relationship(
+ back_populates="apt_dependencies", link_model=APTDependencyPacscriptLink
+ )
+
+ type: DependencyType = Field(
+ sa_column=Column(SQLAlchemyEnum(DependencyType)), primary_key=True
+ )
+
+
+class PacscriptDependencyLink(SQLModel, table=True):
+ """
+ Link between a pacscript dependency and a pacscript.
+
+ Attributes
+ ----------
+ pacscript_name
+ Name of the pacscript.
+ dependency_name
+ Name of the dependency.
+ """
+
+ pacscript_name: Optional[str] = Field(
+ default=None, foreign_key="pacscript.name", primary_key=True
+ )
+ dependency_name: Optional[str] = Field(
+ default=None, foreign_key="pacscriptdependency.name", primary_key=True
+ )
+
+
+class PacscriptDependency(SQLModel, table=True):
+ """
+ SQLModel of a pacscript dependency of a pacscript.
+
+ Attributes
+ ----------
+ name
+ Name of the dependency.
+ dependents
+ List of pacscripts that depend on this dependency.
+ type
+ Type of dependency.
+ """
+
+ name: str = Field(primary_key=True)
+ dependents: List["Pacscript"] = Relationship(
+ back_populates="pacscript_dependencies", link_model=PacscriptDependencyLink
+ )
+ type: DependencyType = Field(
+ sa_column=Column(SQLAlchemyEnum(DependencyType)), primary_key=True
+ )
+
+
+class Pacscript(SQLModel, table=True):
+ """
+ SQLModel to access and write to the Pacscript database.
+
+ There are two types of pacscripts stored in this table, one whose
+ `is_installed` is True, and the other whose `is_installed` is False.
+ `is_installed` is True for pacscripts that are installed, and False for
+ pacscripts that are not installed, which are from the sources.
+
+ Attributes
+ ----------
+ name
+ The name of the pacscript.
+ version
+ The version of the pacscript.
+ url
+ The URL of the pacscript.
+ homepage
+ The homepage of the pacscript.
+ description
+ The description of the pacscript.
+ installed_size
+ The installed size of the pacscript's package in bytes.
+ download_size
+ The downloaded size of the pacscript's package in bytes.
+ date
+ The date the pacscript was last updated.
+ installed_status
+ The installed status of the pacscript.
+ apt_dependencies
+ The list of apt dependencies of the pacscript.
+ apt_optional_dependencies
+ The list of apt optional dependencies of the pacscript.
+ pacscript_dependencies
+ The list of pacscript dependencies of the pacscript.
+ pacscript_optional_dependencies
+ The list of pacscript optional dependencies of the pacscript.
+ repology
+ The repology filters for the pacscript.
+ maintainer
+ The maintainer of the pacscript.
+ is_installed
+ Boolean to signify if the pacscript is installed.
+ """
+
+ name: str = Field(index=True, primary_key=True)
+ version: str
+ url: str
+ homepage: Optional[str] = None
+ description: str
+ installed_size: Optional[int] = None
+ download_size: int
+ date: datetime
+ installed_status: Optional[InstalledStatus] = Field(
+ default=None, sa_column=Column(SQLAlchemyEnum(InstalledStatus))
+ )
+ apt_dependencies: Optional[List[APTDependency]] = Relationship(
+ back_populates="dependents",
+ link_model=APTDependencyPacscriptLink,
+ )
+ apt_optional_dependencies: Optional[Dict[str, str]] = Field(
+ default=None, sa_column=Column(JSON)
+ )
+ pacscript_dependencies: Optional[List[PacscriptDependency]] = Relationship(
+ back_populates="dependents",
+ link_model=PacscriptDependencyLink,
+ )
+ pacscript_optional_dependencies: Optional[Dict[str, str]] = Field(
+ default=None, sa_column=Column(JSON)
+ )
+ repology: Optional[Dict[str, str]] = Field(default=None, sa_column=Column(JSON))
+ maintainer: Optional[str] = None
+ is_installed: bool = Field(primary_key=True)
diff --git a/tests/test_cache.py b/tests/test_cache.py
new file mode 100644
index 0000000..e2ddc21
--- /dev/null
+++ b/tests/test_cache.py
@@ -0,0 +1,268 @@
+# __ _ __ ____ __ ____
+# / / (_) /_ / __ \____ ___________/ /_____ _/ / /
+# / / / / __ \/ /_/ / __ `/ ___/ ___/ __/ __ `/ / /
+# / /___/ / /_/ / ____/ /_/ / /__(__ ) /_/ /_/ / / /
+# /_____/_/_.___/_/ \__,_/\___/____/\__/\__,_/_/_/
+#
+# Copyright (C) 2022-present
+#
+# This file is part of LibPacstall.
+#
+# LibPacstall is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# LibPacstall is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# LibPacstall. If not, see .
+
+"""Test suit for the cache module."""
+
+from datetime import datetime
+from typing import Generator
+
+import pytest
+from sqlalchemy.exc import IntegrityError # type: ignore[misc]
+from sqlmodel import Session, SQLModel, create_engine, select
+
+from libpacstall.cache import (
+ APTDependency,
+ DependencyType,
+ InstalledStatus,
+ Pacscript,
+ PacscriptDependency,
+)
+
+
+@pytest.fixture
+def session() -> Generator[Session, None, None]:
+ """
+ Create a session for the tests.
+
+ Yields
+ ------
+ Session
+ The database session.
+ """
+
+ engine = create_engine("sqlite://", echo=True)
+ SQLModel.metadata.create_all(engine)
+ yield Session(engine)
+
+
+class TestWrites:
+ """Test that writing to the database works."""
+
+ def test_full_write(self, session: Session) -> None:
+ """
+ Test that we can write a full records to the database.
+
+ Parameters
+ ----------
+ session
+ The session database to use (Fixture)
+ """
+
+ with session:
+ session.add(
+ Pacscript(
+ name="foo",
+ version="1.0",
+ url="https://foo.bar",
+ homepage="https://foo.bar",
+ description="baz",
+ installed_size=420,
+ download_size=69,
+ date=datetime.now(),
+ apt_dependencies=[
+ APTDependency(name="foo", type=DependencyType.REQUIRED)
+ ],
+ apt_optional_dependencies={"bar": "baz"},
+ pacscript_dependencies=[
+ PacscriptDependency(name="foo", type=DependencyType.REQUIRED)
+ ],
+ pacscript_optional_dependencies={"bit": "bat"},
+ installed_status=InstalledStatus.DIRECT,
+ repology={"project": "foo", "visiblename": "bar"},
+ maintainer="baz ",
+ is_installed=True,
+ )
+ )
+
+ session.commit()
+
+ def test_partial_write(self, session: Session) -> None:
+ """
+ Test that partial writes to the database are possible.
+
+ Parameters
+ ----------
+ session
+ The session to use (Fixture)
+ """
+
+ with session:
+ session.add(
+ Pacscript(
+ name="foo",
+ version="1.0",
+ url="https://foo.bar",
+ description="baz",
+ download_size=69,
+ date=datetime.now(),
+ is_installed=True,
+ )
+ )
+ session.commit()
+
+ def test_session_integrity(self, session: Session) -> None:
+ """
+ Test that we can't add two pacscripts of the same name and is_installed
+ status.
+
+ Parameters
+ ----------
+ session
+ The session to use (Fixture)
+ """
+
+ with session:
+ session.add(
+ Pacscript(
+ name="foo",
+ version="1.0",
+ url="https://foo.bar",
+ homepage="https://foo.bar",
+ description="baz",
+ installed_size=420,
+ download_size=69,
+ date=datetime.now(),
+ apt_dependencies=[
+ APTDependency(name="foo", type=DependencyType.REQUIRED)
+ ],
+ apt_optional_dependencies={"bar": "baz"},
+ installed_status=InstalledStatus.DIRECT,
+ repology={"project": "foo", "visiblename": "bar"},
+ maintainer="baz ",
+ is_installed=True,
+ )
+ )
+
+ session.add(
+ Pacscript(
+ name="foo",
+ version="1.0",
+ url="https://foo.bar",
+ homepage="https://foo.bar",
+ description="baz",
+ installed_size=420,
+ download_size=69,
+ date=datetime.now(),
+ apt_dependencies=[
+ APTDependency(name="foo", type=DependencyType.REQUIRED)
+ ],
+ apt_optional_dependencies={"bar": "baz"},
+ installed_status=InstalledStatus.DIRECT,
+ repology={"project": "foo", "visiblename": "bar"},
+ maintainer="baz ",
+ is_installed=True,
+ )
+ )
+ with pytest.raises(IntegrityError):
+ session.commit()
+
+
+class TestReads:
+ """Test that reading from the database works."""
+
+ def test_read_all(self, session: Session) -> None:
+ """
+ Test that we can read all pacscripts from the database.
+
+ Parameters
+ ----------
+ session
+ The session to use (Fixture)
+ """
+
+ installed_pacsript = Pacscript(
+ name="foo",
+ version="1.0",
+ url="https://foo.bar",
+ description="baz",
+ download_size=69,
+ date=datetime.now(),
+ is_installed=True,
+ )
+
+ source_pacscript = Pacscript(
+ name="foo",
+ version="1.0",
+ url="https://foo.bar",
+ description="baz",
+ download_size=69,
+ date=datetime.now(),
+ is_installed=False,
+ )
+
+ with session:
+ session.add(installed_pacsript)
+ session.add(source_pacscript)
+ session.commit()
+
+ installed_pacscript_list = session.exec(
+ select(Pacscript).where(Pacscript.is_installed == True)
+ ).all()
+
+ source_pacscript_list = session.exec(
+ select(Pacscript).where(Pacscript.is_installed == False)
+ ).all()
+
+ assert len(session.exec(select(Pacscript)).all()) == 2
+
+ assert len(installed_pacscript_list) == 1
+ assert len(source_pacscript_list) == 1
+
+ def test_read_by_name(self, session: Session) -> None:
+ """
+ Test that we can read a pacscript by name from the database.
+
+ Parameters
+ ----------
+ session
+ The session to use (Fixture)
+ """
+
+ with session:
+ session.add(
+ Pacscript(
+ name="foo",
+ version="1.0",
+ url="https://foo.bar",
+ description="baz",
+ download_size=69,
+ date=datetime.now(),
+ is_installed=True,
+ )
+ )
+
+ session.add(
+ Pacscript(
+ name="bar",
+ version="1.0",
+ url="https://foo.bar",
+ description="baz",
+ download_size=69,
+ date=datetime.now(),
+ is_installed=False,
+ )
+ )
+
+ session.commit()
+
+ assert len(session.query(Pacscript).filter(Pacscript.name == "bar").all()) == 1
+ assert len(session.query(Pacscript).filter(Pacscript.name == "foo").all()) == 1