diff --git a/backend/app/.coveragerc b/backend/app/.coveragerc
index f0e3d4f4de5bf2c00c1c7161b3d58a818a52b5b0..26b1eea7ddc1eb3d1d2fcb343ca048f66d7b8da4 100644
--- a/backend/app/.coveragerc
+++ b/backend/app/.coveragerc
@@ -12,3 +12,7 @@ omit =
     app/dependency_injections.py
     # omit start of app
     app/main.py
+[report]
+exclude_lines =
+    pragma: no cover
+    raise NotImplementedError
diff --git a/backend/app/app/api/endpoints/catalogs.py b/backend/app/app/api/endpoints/catalogs.py
index c3bfe9812e709e5a22571562eb4b4f3c25855fe1..089e73bc01af5fc0b4224e12eca025a45fea1ed4 100644
--- a/backend/app/app/api/endpoints/catalogs.py
+++ b/backend/app/app/api/endpoints/catalogs.py
@@ -45,9 +45,9 @@ async def create_catalog(
     return catalog
 
 
-@router.put("/", response_model=CatalogUpdate)
+@router.put("/", response_model=CatalogRead)
 async def update_catalog(
-    catalog: CatalogCreate, session: Session = Depends(get_session)
+    catalog: CatalogUpdate, session: Session = Depends(get_session)
 ):
     use_case = CrudCatalogUseCase()
     try:
diff --git a/backend/app/app/api/endpoints/eggnogs.py b/backend/app/app/api/endpoints/eggnogs.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b196234fb050f611e9e328998608e1ea696a2e3
--- /dev/null
+++ b/backend/app/app/api/endpoints/eggnogs.py
@@ -0,0 +1,66 @@
+from typing import List
+
+from fastapi import Depends, APIRouter, HTTPException
+from sqlalchemy.exc import NoResultFound, IntegrityError
+from sqlmodel import Session
+
+from app.db import get_session
+from app.core.schemas.entities.eggnog import EggnogRead, EggnogUpdate, EggnogCreate
+from app.core.use_cases.crud.eggnog import CrudEggnogUseCase
+
+
+router = APIRouter()
+
+
+@router.get("/", response_model=List[EggnogRead])
+async def get_eggnogs(session: Session = Depends(get_session)):
+    use_case = CrudEggnogUseCase()
+    eggnogs = use_case.get_all(session=session)
+    return eggnogs
+
+
+@router.get("/{eggnog_id}", response_model=EggnogRead)
+async def get_eggnog(eggnog_id: str, session: Session = Depends(get_session)):
+    use_case = CrudEggnogUseCase()
+    try:
+        eggnog = use_case.get_eggnog(eggnog_id, session=session)
+    except NoResultFound:
+        raise HTTPException(status_code=404, detail=f"Eggnog [{eggnog_id}] not found.")
+    return eggnog
+
+
+@router.post("/", response_model=EggnogRead)
+async def create_eggnog(eggnog: EggnogCreate, session: Session = Depends(get_session)):
+    use_case = CrudEggnogUseCase()
+    eggnog = use_case.create_eggnog(eggnog, session=session)
+    return eggnog
+
+
+@router.put("/", response_model=EggnogRead)
+async def update_eggnog(
+    eggnog: EggnogUpdate,
+    exclude_none: bool = True,
+    session: Session = Depends(get_session),
+):
+    use_case = CrudEggnogUseCase()
+    try:
+        eggnog = use_case.update_eggnog(
+            eggnog, exclude_none=exclude_none, session=session
+        )
+    except NoResultFound:
+        raise HTTPException(
+            status_code=404, detail=f"Eggnog [{eggnog.eggnog_id}] not found."
+        )
+    except IntegrityError:
+        raise HTTPException(status_code=422, detail="Missing data in input data.")
+    return eggnog
+
+
+@router.delete("/{eggnog_id}")
+async def delete_eggnog(eggnog_id: str, session: Session = Depends(get_session)):
+    use_case = CrudEggnogUseCase()
+    try:
+        use_case.delete_eggnog(eggnog_id, session=session)
+    except NoResultFound:
+        raise HTTPException(status_code=404, detail=f"Eggnog [{eggnog_id}] not found.")
+    return {"deleted": True}
diff --git a/backend/app/app/api/endpoints/experiments.py b/backend/app/app/api/endpoints/experiments.py
index b64d745b66ef83df15bbf351c86f2ed42fbb4c67..36e363097e308977d96bc997d26b2373af375ff3 100644
--- a/backend/app/app/api/endpoints/experiments.py
+++ b/backend/app/app/api/endpoints/experiments.py
@@ -1,11 +1,16 @@
 from typing import List
 
-from fastapi import Depends, APIRouter
-from sqlmodel import Session, select
+from fastapi import Depends, APIRouter, HTTPException
+from sqlalchemy.exc import NoResultFound, IntegrityError
+from sqlmodel import Session
 
 from app.db import get_session
-from app.core.models.experiment import Experiment
-from app.core.schemas.entities.experiment import ExperimentRead
+from app.core.schemas.entities.experiment import (
+    ExperimentRead,
+    ExperimentUpdate,
+    ExperimentCreate,
+)
+from app.core.use_cases.crud.experiment import CrudExperimentUseCase
 
 
 router = APIRouter()
@@ -13,5 +18,61 @@ router = APIRouter()
 
 @router.get("/", response_model=List[ExperimentRead])
 async def get_experiments(session: Session = Depends(get_session)):
-    experiments = session.exec(select(Experiment)).all()
+    use_case = CrudExperimentUseCase()
+    experiments = use_case.get_all(session=session)
     return experiments
+
+
+@router.get("/{experiment_id}", response_model=ExperimentRead)
+async def get_experiment(experiment_id: int, session: Session = Depends(get_session)):
+    use_case = CrudExperimentUseCase()
+    try:
+        experiment = use_case.get_experiment(experiment_id, session=session)
+    except NoResultFound:
+        raise HTTPException(
+            status_code=404, detail=f"Experiment [{experiment_id}] not found."
+        )
+    return experiment
+
+
+@router.post("/", response_model=ExperimentRead)
+async def create_experiment(
+    experiment: ExperimentCreate, session: Session = Depends(get_session)
+):
+    use_case = CrudExperimentUseCase()
+    experiment = use_case.create_experiment(experiment, session=session)
+    return experiment
+
+
+@router.put("/", response_model=ExperimentRead)
+async def update_experiment(
+    experiment: ExperimentUpdate,
+    exclude_none: bool = True,
+    session: Session = Depends(get_session),
+):
+    use_case = CrudExperimentUseCase()
+    try:
+        experiment = use_case.update_experiment(
+            experiment, exclude_none=exclude_none, session=session
+        )
+    except NoResultFound:
+        raise HTTPException(
+            status_code=404, detail=f"Experiment [{experiment.id}] not found."
+        )
+    except IntegrityError:
+        raise HTTPException(status_code=422, detail="Missing data in input data.")
+    return experiment
+
+
+@router.delete("/{experiment_id}")
+async def delete_experiment(
+    experiment_id: int, session: Session = Depends(get_session)
+):
+    use_case = CrudExperimentUseCase()
+    try:
+        use_case.delete_experiment(experiment_id, session=session)
+    except NoResultFound:
+        raise HTTPException(
+            status_code=404, detail=f"Experiment [{experiment_id}] not found."
+        )
+    return {"deleted": True}
diff --git a/backend/app/app/api/endpoints/keggs.py b/backend/app/app/api/endpoints/keggs.py
new file mode 100644
index 0000000000000000000000000000000000000000..c60fc2a4226f6d5cd59e534dfd1d10f5963fcae9
--- /dev/null
+++ b/backend/app/app/api/endpoints/keggs.py
@@ -0,0 +1,79 @@
+from typing import List
+
+from fastapi import Depends, APIRouter, HTTPException
+from sqlalchemy.exc import NoResultFound, IntegrityError
+from sqlmodel import Session
+
+from app.db import get_session
+from app.core.schemas.entities.kegg import (
+    KeggOrthologyRead,
+    KeggOrthologyUpdate,
+    KeggOrthologyCreate,
+)
+from app.core.use_cases.crud.kegg import CrudKeggOrthologyUseCase
+
+
+router = APIRouter()
+
+
+@router.get("/", response_model=List[KeggOrthologyRead])
+async def get_kegg_orthology_entries(session: Session = Depends(get_session)):
+    use_case = CrudKeggOrthologyUseCase()
+    keggs = use_case.get_all(session=session)
+    return keggs
+
+
+@router.get("/{kegg_id}", response_model=KeggOrthologyRead)
+async def get_kegg_orthology(kegg_id: str, session: Session = Depends(get_session)):
+    use_case = CrudKeggOrthologyUseCase()
+    try:
+        kegg = use_case.get_kegg(kegg_id, session=session)
+    except NoResultFound:
+        raise HTTPException(
+            status_code=404, detail=f"KeggOrthology [{kegg_id}] not found."
+        )
+    return kegg
+
+
+@router.post("/", response_model=KeggOrthologyRead)
+async def create_kegg_orthology(
+    kegg: KeggOrthologyCreate, session: Session = Depends(get_session)
+):
+    use_case = CrudKeggOrthologyUseCase()
+    try:
+        kegg = use_case.create_kegg(kegg, session=session)
+    except IntegrityError:
+        raise HTTPException(
+            status_code=403, detail=f"KeggOrthology [{kegg.name}] already exists."
+        )
+    return kegg
+
+
+@router.put("/", response_model=KeggOrthologyRead)
+async def update_kegg_orthology(
+    kegg: KeggOrthologyUpdate,
+    exclude_none: bool = True,
+    session: Session = Depends(get_session),
+):
+    use_case = CrudKeggOrthologyUseCase()
+    try:
+        kegg = use_case.update_kegg(kegg, session=session, exclude_none=exclude_none)
+    except NoResultFound:
+        raise HTTPException(
+            status_code=404, detail=f"KeggOrthology [{kegg.kegg_id}] not found."
+        )
+    except IntegrityError:
+        raise HTTPException(status_code=422, detail="Missing data in input data.")
+    return kegg
+
+
+@router.delete("/{kegg_id}")
+async def delete_kegg_orthology(kegg_id: str, session: Session = Depends(get_session)):
+    use_case = CrudKeggOrthologyUseCase()
+    try:
+        use_case.delete_kegg(kegg_id, session=session)
+    except NoResultFound:
+        raise HTTPException(
+            status_code=404, detail=f"KeggOrthology [{kegg_id}] not found."
+        )
+    return {"deleted": True}
diff --git a/backend/app/app/api/endpoints/ncbi_taxonomy.py b/backend/app/app/api/endpoints/ncbi_taxonomy.py
new file mode 100644
index 0000000000000000000000000000000000000000..a8c2c6481888ad30036dbfd41d084631156d7fc6
--- /dev/null
+++ b/backend/app/app/api/endpoints/ncbi_taxonomy.py
@@ -0,0 +1,82 @@
+from typing import List
+
+from fastapi import Depends, APIRouter, HTTPException
+from sqlalchemy.exc import NoResultFound, IntegrityError
+from sqlmodel import Session
+
+from app.db import get_session
+from app.core.schemas.entities.ncbi_taxonomy import (
+    NcbiTaxonomyRead,
+    NcbiTaxonomyUpdate,
+    NcbiTaxonomyCreate,
+)
+from app.core.use_cases.crud.ncbi_taxonomy import CrudNcbiTaxonomyUseCase
+
+
+router = APIRouter()
+
+
+@router.get("/", response_model=List[NcbiTaxonomyRead])
+async def get_ncbi_taxonomy_entries(session: Session = Depends(get_session)):
+    use_case = CrudNcbiTaxonomyUseCase()
+    ncbi_taxonomys = use_case.get_all(session=session)
+    return ncbi_taxonomys
+
+
+@router.get("/{tax_id}", response_model=NcbiTaxonomyRead)
+async def get_ncbi_taxonomy(tax_id: str, session: Session = Depends(get_session)):
+    use_case = CrudNcbiTaxonomyUseCase()
+    try:
+        ncbi_taxonomy = use_case.get_ncbi_taxonomy(tax_id, session=session)
+    except NoResultFound:
+        raise HTTPException(
+            status_code=404, detail=f"NcbiTaxonomy [{tax_id}] not found."
+        )
+    return ncbi_taxonomy
+
+
+@router.post("/", response_model=NcbiTaxonomyRead)
+async def create_ncbi_taxonomy(
+    ncbi_taxonomy: NcbiTaxonomyCreate, session: Session = Depends(get_session)
+):
+    use_case = CrudNcbiTaxonomyUseCase()
+    try:
+        ncbi_taxonomy = use_case.create_ncbi_taxonomy(ncbi_taxonomy, session=session)
+    except IntegrityError:
+        raise HTTPException(
+            status_code=403,
+            detail=f"NcbiTaxonomy [{ncbi_taxonomy.name}] already exists.",
+        )
+    return ncbi_taxonomy
+
+
+@router.put("/", response_model=NcbiTaxonomyRead)
+async def update_ncbi_taxonomy(
+    ncbi_taxonomy: NcbiTaxonomyUpdate,
+    exclude_none: bool = True,
+    session: Session = Depends(get_session),
+):
+    use_case = CrudNcbiTaxonomyUseCase()
+    try:
+        ncbi_taxonomy = use_case.update_ncbi_taxonomy(
+            ncbi_taxonomy, session=session, exclude_none=exclude_none
+        )
+    except NoResultFound:
+        raise HTTPException(
+            status_code=404, detail=f"NcbiTaxonomy [{ncbi_taxonomy.tax_id}] not found."
+        )
+    except IntegrityError:
+        raise HTTPException(status_code=422, detail="Missing data in input data.")
+    return ncbi_taxonomy
+
+
+@router.delete("/{tax_id}")
+async def delete_ncbi_taxonomy(tax_id: str, session: Session = Depends(get_session)):
+    use_case = CrudNcbiTaxonomyUseCase()
+    try:
+        use_case.delete_ncbi_taxonomy(tax_id, session=session)
+    except NoResultFound:
+        raise HTTPException(
+            status_code=404, detail=f"NcbiTaxonomy [{tax_id}] not found."
+        )
+    return {"deleted": True}
diff --git a/backend/app/app/api/router.py b/backend/app/app/api/router.py
index 3dfd0e244c3fa849c104c940737d2b013fbaee93..ea56edd711309ccc40d67e3d34afed858f5168bf 100644
--- a/backend/app/app/api/router.py
+++ b/backend/app/app/api/router.py
@@ -1,6 +1,13 @@
 from fastapi import APIRouter
 
-from app.api.endpoints import catalogs, experiments, genes
+from app.api.endpoints import (
+    catalogs,
+    experiments,
+    genes,
+    keggs,
+    ncbi_taxonomy,
+    eggnogs,
+)
 
 api_router = APIRouter()
 api_router.include_router(genes.router, prefix="/genes", tags=["Genes"])
@@ -8,3 +15,10 @@ api_router.include_router(catalogs.router, prefix="/catalogs", tags=["Catalogs"]
 api_router.include_router(
     experiments.router, prefix="/experiments", tags=["Experiments"]
 )
+api_router.include_router(
+    keggs.router, prefix="/kegg-orthology", tags=["KeggOrthology"]
+)
+api_router.include_router(eggnogs.router, prefix="/eggnog", tags=["EggNOG"])
+api_router.include_router(
+    ncbi_taxonomy.router, prefix="/ncbi_taxonomy", tags=["NCBI Taxonomy"]
+)
diff --git a/backend/app/app/cli/create_test_db.py b/backend/app/app/cli/create_test_db.py
index bc4025cd30feedd265a22a6bf4115e38dde4d3be..5c2e4ebfcc39a2e69415cb6bec19ceb6d61c5aa2 100644
--- a/backend/app/app/cli/create_test_db.py
+++ b/backend/app/app/cli/create_test_db.py
@@ -19,11 +19,11 @@ def create_db():
         catalog_virgo = models.Catalog(name="Virgo")
         catalog_igc = models.Catalog(name="IGC")
         # Experiments
-        experiment_blast = models.Experiment(name="exp A")
-        experiment_kraken = models.Experiment(name="exp B")
+        experiment_blast = models.Experiment(name="exp A", date="2020-10-10")
+        experiment_kraken = models.Experiment(name="exp B", date="2020-10-15")
         # Annotation entities
-        kegg_a = models.Kegg(kegg_id="K00001", name="First KO")
-        kegg_b = models.Kegg(kegg_id="K00002", name="Second KO")
+        kegg_a = models.KeggOrthology(kegg_id="K00001", name="First KO")
+        kegg_b = models.KeggOrthology(kegg_id="K00002", name="Second KO")
         ncbi_tax_ecoli = models.NcbiTaxonomy(
             tax_id=562, name="Escherichia coli", rank="species"
         )
@@ -31,8 +31,12 @@ def create_db():
             tax_id=1578, name="Lactobacillus", rank="genus"
         )
         # Annotations
-        kegg_annot_a = models.KeggAnnotation(kegg=kegg_a, experiment=experiment_blast)
-        kegg_annot_b = models.KeggAnnotation(kegg=kegg_b, experiment=experiment_blast)
+        kegg_annot_a = models.KeggOrthologyAnnotation(
+            kegg=kegg_a, experiment=experiment_blast
+        )
+        kegg_annot_b = models.KeggOrthologyAnnotation(
+            kegg=kegg_b, experiment=experiment_blast
+        )
         ncbi_tax_annot_ecoli = models.NcbiTaxonomyAnnotation(
             ncbi_taxonomy=ncbi_tax_ecoli, experiment=experiment_kraken
         )
diff --git a/backend/app/app/cli/empty_db.py b/backend/app/app/cli/empty_db.py
index 84b6d8335bdf766272d6fc184bd0dd5caa905a7a..8f425f6b1289472f77858827eeb6ccca36f1c34f 100644
--- a/backend/app/app/cli/empty_db.py
+++ b/backend/app/app/cli/empty_db.py
@@ -18,10 +18,10 @@ def empty_all_tables():
     with Session(engine) as session:
         for table in [
             models.NcbiTaxonomyAnnotation,
-            models.KeggAnnotation,
+            models.KeggOrthologyAnnotation,
             models.EggnogAnnotation,
             models.Experiment,
-            models.Kegg,
+            models.KeggOrthology,
             models.Eggnog,
             models.NcbiTaxonomy,
             models.Catalog,
diff --git a/backend/app/app/core/models/__init__.py b/backend/app/app/core/models/__init__.py
index 53ce8103996b5628397eb90e15934f0835f26abb..c6131a8ed4dd385259c467733cb8a25beadfe022 100644
--- a/backend/app/app/core/models/__init__.py
+++ b/backend/app/app/core/models/__init__.py
@@ -1,5 +1,5 @@
 from .annotations import (  # noqa
-    KeggAnnotation,
+    KeggOrthologyAnnotation,
     EggnogAnnotation,
     NcbiTaxonomyAnnotation,
 )
@@ -7,11 +7,11 @@ from .catalog import Catalog  # noqa
 from .experiment import Experiment  # noqa
 from .gene import Gene  # noqa
 from .ncbi_taxonomy import NcbiTaxonomy  # noqa
-from .kegg import Kegg  # noqa
+from .kegg import KeggOrthology  # noqa
 from .eggnog import Eggnog  # noqa
 from .gene_links import (  # noqa
     GeneNcbiTaxonomyLink,
     GeneEggnogAnnotationLink,
-    GeneKeggAnnotationLink,
+    GeneKeggOrthologyAnnotationLink,
     GeneCatalogLink,
 )
diff --git a/backend/app/app/core/models/annotations.py b/backend/app/app/core/models/annotations.py
index e759fcd437714f8af6edd1e1f80754429e92915b..b16f457d0180c00a72c33f31adee857d54018e77 100644
--- a/backend/app/app/core/models/annotations.py
+++ b/backend/app/app/core/models/annotations.py
@@ -3,7 +3,7 @@ from typing import List
 from sqlmodel import Field, SQLModel, Relationship
 
 from app.core.models.gene_links import (
-    GeneKeggAnnotationLink,
+    GeneKeggOrthologyAnnotationLink,
     GeneEggnogAnnotationLink,
     GeneNcbiTaxonomyLink,
 )
@@ -16,17 +16,17 @@ class AnnotationBase(SQLModel):
 # ------------------ KEGG ------------------
 
 
-class KeggAnnotationBase(AnnotationBase):
-    kegg_id: int = Field(foreign_key="kegg.id")
+class KeggOrthologyAnnotationBase(AnnotationBase):
+    kegg_id: int = Field(foreign_key="keggorthology.id")
 
 
-class KeggAnnotation(KeggAnnotationBase, table=True):
+class KeggOrthologyAnnotation(KeggOrthologyAnnotationBase, table=True):
     id: int = Field(default=None, primary_key=True)
 
     genes: List["Gene"] = Relationship(
-        back_populates="kegg_annotations", link_model=GeneKeggAnnotationLink
+        back_populates="kegg_annotations", link_model=GeneKeggOrthologyAnnotationLink
     )
-    kegg: "Kegg" = Relationship(back_populates="annotations")
+    kegg: "KeggOrthology" = Relationship(back_populates="annotations")
     experiment: "Experiment" = Relationship(back_populates="kegg_annotations")
 
 
diff --git a/backend/app/app/core/models/experiment.py b/backend/app/app/core/models/experiment.py
index 3a26bb41df7f8d22e58ff0eb006ba007e471146e..b08d45d116f8b340efcff7cedb7b11424a7fa055 100644
--- a/backend/app/app/core/models/experiment.py
+++ b/backend/app/app/core/models/experiment.py
@@ -1,3 +1,4 @@
+import datetime
 from typing import List, Optional
 
 from sqlmodel import Field, SQLModel, Relationship
@@ -5,6 +6,7 @@ from sqlmodel import Field, SQLModel, Relationship
 
 class ExperimentBase(SQLModel):
     name: str = Field(index=False)
+    date: datetime.date = Field(index=False)
     description: Optional[str] = Field(index=False)
     doi: Optional[str] = Field(index=False)
 
@@ -12,10 +14,18 @@ class ExperimentBase(SQLModel):
 class Experiment(ExperimentBase, table=True):
     id: int = Field(default=None, primary_key=True)
 
-    kegg_annotations: List["KeggAnnotation"] = Relationship(back_populates="experiment")
+    kegg_annotations: List["KeggOrthologyAnnotation"] = Relationship(
+        back_populates="experiment"
+    )
     eggnog_annotations: List["EggnogAnnotation"] = Relationship(
         back_populates="experiment"
     )
     ncbi_taxonomy_annotations: List["NcbiTaxonomyAnnotation"] = Relationship(
         back_populates="experiment"
     )
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__}: {self.name} (id={self.id}, {self.date})>"
+
+    def __str__(self) -> str:
+        return f"<{self.__class__.__name__}: {self.name} (id={self.id}, {self.date})>"
diff --git a/backend/app/app/core/models/gene.py b/backend/app/app/core/models/gene.py
index f661572096e6df87a79dac8ac8f2ff0bdf0bf7d2..f3289ac973aebed4699b8fb4483d9bea31e3ef52 100644
--- a/backend/app/app/core/models/gene.py
+++ b/backend/app/app/core/models/gene.py
@@ -4,7 +4,7 @@ from sqlmodel import Field, SQLModel, Relationship
 
 from app.core.models.gene_links import (
     GeneCatalogLink,
-    GeneKeggAnnotationLink,
+    GeneKeggOrthologyAnnotationLink,
     GeneEggnogAnnotationLink,
     GeneNcbiTaxonomyLink,
 )
@@ -22,8 +22,8 @@ class Gene(GeneBase, table=True):
     eggnog_annotations: List["EggnogAnnotation"] = Relationship(
         back_populates="genes", link_model=GeneEggnogAnnotationLink
     )
-    kegg_annotations: List["KeggAnnotation"] = Relationship(
-        back_populates="genes", link_model=GeneKeggAnnotationLink
+    kegg_annotations: List["KeggOrthologyAnnotation"] = Relationship(
+        back_populates="genes", link_model=GeneKeggOrthologyAnnotationLink
     )
     ncbi_taxonomy_annotations: List["NcbiTaxonomyAnnotation"] = Relationship(
         back_populates="genes", link_model=GeneNcbiTaxonomyLink
diff --git a/backend/app/app/core/models/gene_links.py b/backend/app/app/core/models/gene_links.py
index 3cdfb0c462f85bd4a5346f52bfc02c7f5034463f..4b8b5b68f65c53174e7766c07cdca8a060af3716 100644
--- a/backend/app/app/core/models/gene_links.py
+++ b/backend/app/app/core/models/gene_links.py
@@ -15,9 +15,9 @@ class GeneCatalogLink(GeneLinkBase, table=True):
     )
 
 
-class GeneKeggAnnotationLink(GeneLinkBase, table=True):
+class GeneKeggOrthologyAnnotationLink(GeneLinkBase, table=True):
     kegg_annotation_id: Optional[int] = Field(
-        default=None, foreign_key="keggannotation.id", primary_key=True
+        default=None, foreign_key="keggorthologyannotation.id", primary_key=True
     )
 
 
diff --git a/backend/app/app/core/models/kegg.py b/backend/app/app/core/models/kegg.py
index 12ebe7ec7bcf7d4723ebe4301d8920071a4c82fd..e1648c12db6527ea0abc2032444e8961fab3a554 100644
--- a/backend/app/app/core/models/kegg.py
+++ b/backend/app/app/core/models/kegg.py
@@ -1,11 +1,11 @@
 from typing import List
 
 from slugify import slugify
-from sqlmodel import Field, Relationship, SQLModel
+from sqlmodel import Field, Relationship, SQLModel, VARCHAR, Column
 
 
-class KeggBase(SQLModel):
-    kegg_id: str
+class KeggOrthologyBase(SQLModel):
+    kegg_id: str = Field(sa_column=Column("kegg_id", VARCHAR, unique=True))
     name: str = Field(index=False)
 
     @property
@@ -13,7 +13,7 @@ class KeggBase(SQLModel):
         return slugify(self.name)
 
 
-class Kegg(KeggBase, table=True):
+class KeggOrthology(KeggOrthologyBase, table=True):
     id: int = Field(default=None, primary_key=True)
 
-    annotations: List["KeggAnnotation"] = Relationship(back_populates="kegg")
+    annotations: List["KeggOrthologyAnnotation"] = Relationship(back_populates="kegg")
diff --git a/backend/app/app/core/models/ncbi_taxonomy.py b/backend/app/app/core/models/ncbi_taxonomy.py
index 4729894071ff19c2820d72b1add8c64b4b1b89f5..e998d1908d3cfc7cade51eac8deffc2156e285c7 100644
--- a/backend/app/app/core/models/ncbi_taxonomy.py
+++ b/backend/app/app/core/models/ncbi_taxonomy.py
@@ -1,11 +1,11 @@
 from typing import List, Optional
 
 import slugify
-from sqlmodel import Field, Relationship, Column, JSON, SQLModel
+from sqlmodel import Field, Relationship, Column, JSON, SQLModel, INTEGER
 
 
 class NcbiTaxonomyBase(SQLModel):
-    tax_id: int
+    tax_id: int = Field(sa_column=Column("tax_id", INTEGER, unique=True))
     name: str = Field(index=False)
     rank: str
 
diff --git a/backend/app/app/core/repositories/catalog.py b/backend/app/app/core/repositories/catalog.py
index 4a1fbc908f47cfcee51b715d29b4c1a12004fc23..f0a01ea39377b8754abc1b8c34bfc226f28aad64 100644
--- a/backend/app/app/core/repositories/catalog.py
+++ b/backend/app/app/core/repositories/catalog.py
@@ -48,10 +48,12 @@ class SqlModelCatalogsRepo(CatalogsRepo):
         session.refresh(catalog)
         return catalog
 
-    def update(self, catalog_input: CatalogUpdate, session: Session) -> Catalog:
+    def update(
+        self, catalog_input: CatalogUpdate, session: Session, exclude_none: bool = True
+    ) -> Catalog:
         """Update a catalog entry."""
         catalog = self.get(catalog_input.name, session)
-        for k, v in catalog_input.dict().items():
+        for k, v in catalog_input.dict(exclude_none=exclude_none).items():
             setattr(catalog, k, v)
         session.add(catalog)
         session.commit()
diff --git a/backend/app/app/core/repositories/eggnog.py b/backend/app/app/core/repositories/eggnog.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee4dde27ad01d7de7cca1a7ca12c535d4419ca82
--- /dev/null
+++ b/backend/app/app/core/repositories/eggnog.py
@@ -0,0 +1,70 @@
+import abc
+from typing import List
+
+from sqlmodel import Session, select
+
+from app.core.models.eggnog import Eggnog
+from app.core.schemas.entities.eggnog import EggnogCreate, EggnogUpdate
+
+
+class EggnogsRepo(abc.ABC):
+    @abc.abstractmethod
+    def get(self, eggnog_name: str) -> Eggnog:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def get_all(self) -> List[Eggnog]:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def create(self, eggnog_input: EggnogCreate) -> Eggnog:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def update(self, eggnog_input: EggnogUpdate) -> Eggnog:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def delete(self, eggnog_name: str):
+        raise NotImplementedError
+
+
+class SqlModelEggnogsRepo(EggnogsRepo):
+    def get(self, eggnog_id, session: Session) -> Eggnog:
+        """Retrieve one eggnog from name."""
+        statement = select(Eggnog).where(Eggnog.eggnog_id == eggnog_id)
+        return session.exec(statement).one()
+
+    def get_all(self, session: Session) -> List[Eggnog]:
+        """Retrieve all eggnogs."""
+        eggnogs = session.exec(select(Eggnog)).all()
+        return eggnogs
+
+    def create(self, eggnog_input: EggnogCreate, session: Session) -> Eggnog:
+        """Create a eggnog entry."""
+        eggnog = Eggnog(
+            eggnog_id=eggnog_input.eggnog_id,
+            name=eggnog_input.name,
+            version=eggnog_input.version,
+        )
+        session.add(eggnog)
+        session.commit()
+        session.refresh(eggnog)
+        return eggnog
+
+    def update(
+        self, eggnog_input: EggnogUpdate, session: Session, exclude_none: bool = True
+    ) -> Eggnog:
+        """Update a eggnog entry."""
+        eggnog = self.get(eggnog_input.eggnog_id, session)
+        for k, v in eggnog_input.dict(exclude_none=exclude_none).items():
+            setattr(eggnog, k, v)
+        session.add(eggnog)
+        session.commit()
+        session.refresh(eggnog)
+        return eggnog
+
+    def delete(self, eggnog_id: str, session: Session):
+        eggnog = self.get(eggnog_id, session)
+        session.delete(eggnog)
+        session.commit()
diff --git a/backend/app/app/core/repositories/experiment.py b/backend/app/app/core/repositories/experiment.py
new file mode 100644
index 0000000000000000000000000000000000000000..b0783bb305af6b2503f0f10d4db79e4775602ab9
--- /dev/null
+++ b/backend/app/app/core/repositories/experiment.py
@@ -0,0 +1,76 @@
+import abc
+from typing import List
+
+from sqlmodel import Session, select
+
+from app.core.models.experiment import Experiment
+from app.core.schemas.entities.experiment import ExperimentCreate, ExperimentUpdate
+
+
+class ExperimentsRepo(abc.ABC):
+    @abc.abstractmethod
+    def get(self, experiment_name: str) -> Experiment:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def get_all(self) -> List[Experiment]:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def create(self, experiment_input: ExperimentCreate) -> Experiment:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def update(self, experiment_input: ExperimentUpdate) -> Experiment:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def delete(self, experiment_name: str):
+        raise NotImplementedError
+
+
+class SqlModelExperimentsRepo(ExperimentsRepo):
+    def get(self, experiment_id, session: Session) -> Experiment:
+        """Retrieve one experiment from name."""
+        statement = select(Experiment).where(Experiment.id == experiment_id)
+        return session.exec(statement).one()
+
+    def get_all(self, session: Session) -> List[Experiment]:
+        """Retrieve all experiments."""
+        experiments = session.exec(select(Experiment)).all()
+        return experiments
+
+    def create(
+        self, experiment_input: ExperimentCreate, session: Session
+    ) -> Experiment:
+        """Create a experiment entry."""
+        experiment = Experiment(
+            name=experiment_input.name,
+            date=experiment_input.date,
+            description=experiment_input.description,
+            doi=experiment_input.doi,
+        )
+        session.add(experiment)
+        session.commit()
+        session.refresh(experiment)
+        return experiment
+
+    def update(
+        self,
+        experiment_input: ExperimentUpdate,
+        session: Session,
+        exclude_none: bool = True,
+    ) -> Experiment:
+        """Update a experiment entry."""
+        experiment = self.get(experiment_input.id, session)
+        for k, v in experiment_input.dict(exclude_none=exclude_none).items():
+            setattr(experiment, k, v)
+        session.add(experiment)
+        session.commit()
+        session.refresh(experiment)
+        return experiment
+
+    def delete(self, experiment_id: str, session: Session):
+        experiment = self.get(experiment_id, session)
+        session.delete(experiment)
+        session.commit()
diff --git a/backend/app/app/core/repositories/kegg.py b/backend/app/app/core/repositories/kegg.py
new file mode 100644
index 0000000000000000000000000000000000000000..882a482b77983902468a4ce9a15ea8a13f2b465b
--- /dev/null
+++ b/backend/app/app/core/repositories/kegg.py
@@ -0,0 +1,71 @@
+import abc
+from typing import List
+
+from sqlmodel import Session, select
+
+from app.core.models.kegg import KeggOrthology
+from app.core.schemas.entities.kegg import KeggOrthologyCreate, KeggOrthologyUpdate
+
+
+class KeggOrthologysRepo(abc.ABC):
+    @abc.abstractmethod
+    def get(self, kegg_name: str) -> KeggOrthology:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def get_all(self) -> List[KeggOrthology]:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def create(self, kegg_input: KeggOrthologyCreate) -> KeggOrthology:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def update(self, kegg_input: KeggOrthologyUpdate) -> KeggOrthology:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def delete(self, kegg_name: str):
+        raise NotImplementedError
+
+
+class SqlModelKeggOrthologysRepo(KeggOrthologysRepo):
+    def get(self, kegg_id, session: Session) -> KeggOrthology:
+        """Retrieve one kegg from name."""
+        statement = select(KeggOrthology).where(KeggOrthology.kegg_id == kegg_id)
+        return session.exec(statement).one()
+
+    def get_all(self, session: Session) -> List[KeggOrthology]:
+        """Retrieve all keggs."""
+        keggs = session.exec(select(KeggOrthology)).all()
+        return keggs
+
+    def create(
+        self, kegg_input: KeggOrthologyCreate, session: Session
+    ) -> KeggOrthology:
+        """Create a kegg entry."""
+        kegg = KeggOrthology(kegg_id=kegg_input.kegg_id, name=kegg_input.name)
+        session.add(kegg)
+        session.commit()
+        session.refresh(kegg)
+        return kegg
+
+    def update(
+        self,
+        kegg_input: KeggOrthologyUpdate,
+        session: Session,
+        exclude_none: bool = True,
+    ) -> KeggOrthology:
+        """Update a kegg entry."""
+        kegg = self.get(kegg_input.kegg_id, session)
+        for k, v in kegg_input.dict(exclude_none=exclude_none).items():
+            setattr(kegg, k, v)
+        session.add(kegg)
+        session.commit()
+        session.refresh(kegg)
+        return kegg
+
+    def delete(self, kegg_id: str, session: Session):
+        kegg = self.get(kegg_id, session)
+        session.delete(kegg)
+        session.commit()
diff --git a/backend/app/app/core/repositories/ncbi_taxonomy.py b/backend/app/app/core/repositories/ncbi_taxonomy.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c4afe7256041113b2df684d503ea111a8ddcdf1
--- /dev/null
+++ b/backend/app/app/core/repositories/ncbi_taxonomy.py
@@ -0,0 +1,78 @@
+import abc
+from typing import List
+
+from sqlmodel import Session, select
+
+from app.core.models.ncbi_taxonomy import NcbiTaxonomy
+from app.core.schemas.entities.ncbi_taxonomy import (
+    NcbiTaxonomyCreate,
+    NcbiTaxonomyUpdate,
+)
+
+
+class NcbiTaxonomysRepo(abc.ABC):
+    @abc.abstractmethod
+    def get(self, ncbi_taxonomy_name: str) -> NcbiTaxonomy:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def get_all(self) -> List[NcbiTaxonomy]:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def create(self, ncbi_taxonomy_input: NcbiTaxonomyCreate) -> NcbiTaxonomy:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def update(self, ncbi_taxonomy_input: NcbiTaxonomyUpdate) -> NcbiTaxonomy:
+        raise NotImplementedError
+
+    @abc.abstractmethod
+    def delete(self, ncbi_taxonomy_name: str):
+        raise NotImplementedError
+
+
+class SqlModelNcbiTaxonomysRepo(NcbiTaxonomysRepo):
+    def get(self, tax_id, session: Session) -> NcbiTaxonomy:
+        """Retrieve one ncbi_taxonomy from name."""
+        statement = select(NcbiTaxonomy).where(NcbiTaxonomy.tax_id == tax_id)
+        return session.exec(statement).one()
+
+    def get_all(self, session: Session) -> List[NcbiTaxonomy]:
+        """Retrieve all ncbi_taxonomys."""
+        ncbi_taxonomys = session.exec(select(NcbiTaxonomy)).all()
+        return ncbi_taxonomys
+
+    def create(
+        self, ncbi_taxonomy_input: NcbiTaxonomyCreate, session: Session
+    ) -> NcbiTaxonomy:
+        """Create a ncbi_taxonomy entry."""
+        ncbi_taxonomy = NcbiTaxonomy(
+            tax_id=ncbi_taxonomy_input.tax_id,
+            rank=ncbi_taxonomy_input.rank,
+            name=ncbi_taxonomy_input.name,
+        )
+        session.add(ncbi_taxonomy)
+        session.commit()
+        session.refresh(ncbi_taxonomy)
+        return ncbi_taxonomy
+
+    def update(
+        self,
+        ncbi_taxonomy_input: NcbiTaxonomyUpdate,
+        session: Session,
+        exclude_none: bool = True,
+    ) -> NcbiTaxonomy:
+        """Update a ncbi_taxonomy entry."""
+        ncbi_taxonomy = self.get(ncbi_taxonomy_input.tax_id, session)
+        for k, v in ncbi_taxonomy_input.dict(exclude_none=exclude_none).items():
+            setattr(ncbi_taxonomy, k, v)
+        session.add(ncbi_taxonomy)
+        session.commit()
+        session.refresh(ncbi_taxonomy)
+        return ncbi_taxonomy
+
+    def delete(self, tax_id: str, session: Session):
+        ncbi_taxonomy = self.get(tax_id, session)
+        session.delete(ncbi_taxonomy)
+        session.commit()
diff --git a/backend/app/app/core/schemas/entities/annotations.py b/backend/app/app/core/schemas/entities/annotations.py
index e09e9b7a5e3c3ebe267e7202b5bbf767cf64480d..a531a6cfa75ff8095f0af31b46bcef526bf62c85 100644
--- a/backend/app/app/core/schemas/entities/annotations.py
+++ b/backend/app/app/core/schemas/entities/annotations.py
@@ -1,13 +1,13 @@
 from sqlmodel import SQLModel
 
 from app.core.models.annotations import (
-    KeggAnnotationBase,
+    KeggOrthologyAnnotationBase,
     EggnogAnnotationBase,
     NcbiTaxonomyAnnotationBase,
 )
 from app.core.schemas.entities.eggnog import EggnogRead
 from app.core.schemas.entities.experiment import ExperimentRead
-from app.core.schemas.entities.kegg import KeggRead
+from app.core.schemas.entities.kegg import KeggOrthologyRead
 from app.core.schemas.entities.ncbi_taxonomy import NcbiTaxonomyRead
 
 
@@ -18,11 +18,11 @@ class BaseAnnotationRead(SQLModel):
 # ------------------ KEGG ------------------
 
 
-class KeggAnnotationRead(BaseAnnotationRead):
-    kegg: KeggRead
+class KeggOrthologyAnnotationRead(BaseAnnotationRead):
+    kegg: KeggOrthologyRead
 
 
-class KeggAnnotationCreate(KeggAnnotationBase):
+class KeggOrthologyAnnotationCreate(KeggOrthologyAnnotationBase):
     pass
 
 
diff --git a/backend/app/app/core/schemas/entities/eggnog.py b/backend/app/app/core/schemas/entities/eggnog.py
index b533a66b8ce334db7496c4f0457d466aab8ce91c..f0995fb5344ed2de353528454670e22f6889eece 100644
--- a/backend/app/app/core/schemas/entities/eggnog.py
+++ b/backend/app/app/core/schemas/entities/eggnog.py
@@ -1,3 +1,5 @@
+from typing import Optional
+
 from app.core.models.eggnog import EggnogBase
 
 
@@ -7,3 +9,9 @@ class EggnogRead(EggnogBase):
 
 class EggnogCreate(EggnogBase):
     pass
+
+
+class EggnogUpdate(EggnogBase):
+    name: Optional[str]
+    version: Optional[str]
+    pass
diff --git a/backend/app/app/core/schemas/entities/experiment.py b/backend/app/app/core/schemas/entities/experiment.py
index efddfde8c6e5569eeaa6d7e687b4bcb2611dee51..6669efb1f276f3ea77c1a980138f501477cde5d9 100644
--- a/backend/app/app/core/schemas/entities/experiment.py
+++ b/backend/app/app/core/schemas/entities/experiment.py
@@ -1,3 +1,6 @@
+import datetime
+from typing import Optional
+
 from app.core.models.experiment import ExperimentBase
 
 
@@ -6,4 +9,12 @@ class ExperimentCreate(ExperimentBase):
 
 
 class ExperimentRead(ExperimentBase):
+    id: int
+    pass
+
+
+class ExperimentUpdate(ExperimentBase):
+    id: int
+    name: Optional[str]
+    date: Optional[datetime.time]
     pass
diff --git a/backend/app/app/core/schemas/entities/kegg.py b/backend/app/app/core/schemas/entities/kegg.py
index bbd565c98e8ccfdb4bb6b6139b4608b606bbd0e5..615b9d379cbde8ab4bf5776cb4339cb1ce2c85cf 100644
--- a/backend/app/app/core/schemas/entities/kegg.py
+++ b/backend/app/app/core/schemas/entities/kegg.py
@@ -1,9 +1,16 @@
-from app.core.models.kegg import KeggBase
+from typing import Optional
 
+from app.core.models.kegg import KeggOrthologyBase
 
-class KeggRead(KeggBase):
+
+class KeggOrthologyRead(KeggOrthologyBase):
+    pass
+
+
+class KeggOrthologyCreate(KeggOrthologyBase):
     pass
 
 
-class KeggCreate(KeggBase):
+class KeggOrthologyUpdate(KeggOrthologyBase):
+    name: Optional[str]
     pass
diff --git a/backend/app/app/core/schemas/entities/ncbi_taxonomy.py b/backend/app/app/core/schemas/entities/ncbi_taxonomy.py
index ff8ac2f3e206f0bd8e3bf2f02f90b5ed28835779..8dc8e5a7e3c178ba4310fc49d59055720d73702f 100644
--- a/backend/app/app/core/schemas/entities/ncbi_taxonomy.py
+++ b/backend/app/app/core/schemas/entities/ncbi_taxonomy.py
@@ -1,3 +1,7 @@
+from typing import Optional
+
+from sqlmodel import SQLModel
+
 from app.core.models.ncbi_taxonomy import NcbiTaxonomyBase
 
 
@@ -7,3 +11,9 @@ class NcbiTaxonomyRead(NcbiTaxonomyBase):
 
 class NcbiTaxonomyCreate(NcbiTaxonomyBase):
     pass
+
+
+class NcbiTaxonomyUpdate(SQLModel):
+    tax_id: int
+    name: Optional[str]
+    rank: Optional[str]
diff --git a/backend/app/app/core/schemas/nested/gene.py b/backend/app/app/core/schemas/nested/gene.py
index dae2618032f5249f5eca519b80954cc7c2ad54f9..48411446ed673642e4b58c4f0696be9c57ef8f7e 100644
--- a/backend/app/app/core/schemas/nested/gene.py
+++ b/backend/app/app/core/schemas/nested/gene.py
@@ -6,7 +6,7 @@ from app.core.schemas.entities.gene import (
 from app.core.schemas.entities.catalog import CatalogRead
 
 from app.core.schemas.entities.annotations import (
-    KeggAnnotationRead,
+    KeggOrthologyAnnotationRead,
     EggnogAnnotationRead,
     NcbiTaxonomyAnnotationRead,
 )
@@ -15,5 +15,5 @@ from app.core.schemas.entities.annotations import (
 class GeneReadWithAnnotations(GeneRead):
     catalogs: List[CatalogRead] = []
     eggnog_annotations: List[EggnogAnnotationRead] = []
-    kegg_annotations: List[KeggAnnotationRead] = []
+    kegg_annotations: List[KeggOrthologyAnnotationRead] = []
     ncbi_taxonomy_annotations: List[NcbiTaxonomyAnnotationRead] = []
diff --git a/backend/app/app/core/use_cases/crud/eggnog.py b/backend/app/app/core/use_cases/crud/eggnog.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf4e76deeeed007d8b4a00104913d0b409023b37
--- /dev/null
+++ b/backend/app/app/core/use_cases/crud/eggnog.py
@@ -0,0 +1,28 @@
+from typing import List
+
+import inject
+
+from app.core.models.eggnog import Eggnog
+from app.core.schemas.entities.eggnog import EggnogCreate, EggnogUpdate
+from app.core.repositories.eggnog import EggnogsRepo
+
+
+class CrudEggnogUseCase:
+    @inject.autoparams("eggnogs_repo")
+    def __init__(self, eggnogs_repo: EggnogsRepo):
+        self._eggnogs_repo = eggnogs_repo
+
+    def create_eggnog(self, eggnog_input: EggnogCreate, **kwargs) -> Eggnog:
+        return self._eggnogs_repo.create(eggnog_input, **kwargs)
+
+    def update_eggnog(self, eggnog_input: EggnogUpdate, **kwargs) -> Eggnog:
+        return self._eggnogs_repo.update(eggnog_input, **kwargs)
+
+    def delete_eggnog(self, eggnog_id: str, **kwargs):
+        return self._eggnogs_repo.delete(eggnog_id, **kwargs)
+
+    def get_eggnog(self, eggnog_id: str, **kwargs) -> Eggnog:
+        return self._eggnogs_repo.get(eggnog_id, **kwargs)
+
+    def get_all(self, **kwargs) -> List[Eggnog]:
+        return self._eggnogs_repo.get_all(**kwargs)
diff --git a/backend/app/app/core/use_cases/crud/experiment.py b/backend/app/app/core/use_cases/crud/experiment.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0f47f9b962b399b03cbd91922d9328cd09e8eaf
--- /dev/null
+++ b/backend/app/app/core/use_cases/crud/experiment.py
@@ -0,0 +1,32 @@
+from typing import List
+
+import inject
+
+from app.core.models.experiment import Experiment
+from app.core.schemas.entities.experiment import ExperimentCreate, ExperimentUpdate
+from app.core.repositories.experiment import ExperimentsRepo
+
+
+class CrudExperimentUseCase:
+    @inject.autoparams("experiments_repo")
+    def __init__(self, experiments_repo: ExperimentsRepo):
+        self._experiments_repo = experiments_repo
+
+    def create_experiment(
+        self, experiment_input: ExperimentCreate, **kwargs
+    ) -> Experiment:
+        return self._experiments_repo.create(experiment_input, **kwargs)
+
+    def update_experiment(
+        self, experiment_input: ExperimentUpdate, **kwargs
+    ) -> Experiment:
+        return self._experiments_repo.update(experiment_input, **kwargs)
+
+    def delete_experiment(self, experiment_id: str, **kwargs):
+        return self._experiments_repo.delete(experiment_id, **kwargs)
+
+    def get_experiment(self, experiment_id: str, **kwargs) -> Experiment:
+        return self._experiments_repo.get(experiment_id, **kwargs)
+
+    def get_all(self, **kwargs) -> List[Experiment]:
+        return self._experiments_repo.get_all(**kwargs)
diff --git a/backend/app/app/core/use_cases/crud/kegg.py b/backend/app/app/core/use_cases/crud/kegg.py
new file mode 100644
index 0000000000000000000000000000000000000000..502824a64294a5e8563b4c12f8a24d1ab9101d44
--- /dev/null
+++ b/backend/app/app/core/use_cases/crud/kegg.py
@@ -0,0 +1,28 @@
+from typing import List
+
+import inject
+
+from app.core.models.kegg import KeggOrthology
+from app.core.schemas.entities.kegg import KeggOrthologyCreate, KeggOrthologyUpdate
+from app.core.repositories.kegg import KeggOrthologysRepo
+
+
+class CrudKeggOrthologyUseCase:
+    @inject.autoparams("keggs_repo")
+    def __init__(self, keggs_repo: KeggOrthologysRepo):
+        self._keggs_repo = keggs_repo
+
+    def create_kegg(self, kegg_input: KeggOrthologyCreate, **kwargs) -> KeggOrthology:
+        return self._keggs_repo.create(kegg_input, **kwargs)
+
+    def update_kegg(self, kegg_input: KeggOrthologyUpdate, **kwargs) -> KeggOrthology:
+        return self._keggs_repo.update(kegg_input, **kwargs)
+
+    def delete_kegg(self, kegg_id: str, **kwargs):
+        return self._keggs_repo.delete(kegg_id, **kwargs)
+
+    def get_kegg(self, kegg_id: str, **kwargs) -> KeggOrthology:
+        return self._keggs_repo.get(kegg_id, **kwargs)
+
+    def get_all(self, **kwargs) -> List[KeggOrthology]:
+        return self._keggs_repo.get_all(**kwargs)
diff --git a/backend/app/app/core/use_cases/crud/ncbi_taxonomy.py b/backend/app/app/core/use_cases/crud/ncbi_taxonomy.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf4d4fd21e922338228073530c4b23e246479864
--- /dev/null
+++ b/backend/app/app/core/use_cases/crud/ncbi_taxonomy.py
@@ -0,0 +1,35 @@
+from typing import List
+
+import inject
+
+from app.core.models.ncbi_taxonomy import NcbiTaxonomy
+from app.core.schemas.entities.ncbi_taxonomy import (
+    NcbiTaxonomyCreate,
+    NcbiTaxonomyUpdate,
+)
+from app.core.repositories.ncbi_taxonomy import NcbiTaxonomysRepo
+
+
+class CrudNcbiTaxonomyUseCase:
+    @inject.autoparams("ncbi_taxonomys_repo")
+    def __init__(self, ncbi_taxonomys_repo: NcbiTaxonomysRepo):
+        self._ncbi_taxonomys_repo = ncbi_taxonomys_repo
+
+    def create_ncbi_taxonomy(
+        self, ncbi_taxonomy_input: NcbiTaxonomyCreate, **kwargs
+    ) -> NcbiTaxonomy:
+        return self._ncbi_taxonomys_repo.create(ncbi_taxonomy_input, **kwargs)
+
+    def update_ncbi_taxonomy(
+        self, ncbi_taxonomy_input: NcbiTaxonomyUpdate, **kwargs
+    ) -> NcbiTaxonomy:
+        return self._ncbi_taxonomys_repo.update(ncbi_taxonomy_input, **kwargs)
+
+    def delete_ncbi_taxonomy(self, ncbi_taxonomy_name: str, **kwargs):
+        return self._ncbi_taxonomys_repo.delete(ncbi_taxonomy_name, **kwargs)
+
+    def get_ncbi_taxonomy(self, ncbi_taxonomy_id: str, **kwargs) -> NcbiTaxonomy:
+        return self._ncbi_taxonomys_repo.get(ncbi_taxonomy_id, **kwargs)
+
+    def get_all(self, **kwargs) -> List[NcbiTaxonomy]:
+        return self._ncbi_taxonomys_repo.get_all(**kwargs)
diff --git a/backend/app/app/dependency_injections.py b/backend/app/app/dependency_injections.py
index 2a409860856c2b130c4c9691b42f631d747c15a9..e8b6d3089569fe5c20cf558e4fe7b8a15cf1a66b 100644
--- a/backend/app/app/dependency_injections.py
+++ b/backend/app/app/dependency_injections.py
@@ -1,11 +1,26 @@
 import inject
 
 from app.core.repositories.catalog import CatalogsRepo, SqlModelCatalogsRepo
+from app.core.repositories.eggnog import EggnogsRepo, SqlModelEggnogsRepo
+from app.core.repositories.experiment import ExperimentsRepo, SqlModelExperimentsRepo
+from app.core.repositories.kegg import KeggOrthologysRepo, SqlModelKeggOrthologysRepo
+from app.core.repositories.ncbi_taxonomy import (
+    NcbiTaxonomysRepo,
+    SqlModelNcbiTaxonomysRepo,
+)
 
 
 def di_configuration(binder):
     binder.bind(CatalogsRepo, SqlModelCatalogsRepo())
+    binder.bind(EggnogsRepo, SqlModelEggnogsRepo())
+    binder.bind(KeggOrthologysRepo, SqlModelKeggOrthologysRepo())
+    binder.bind(NcbiTaxonomysRepo, SqlModelNcbiTaxonomysRepo())
+    binder.bind(ExperimentsRepo, SqlModelExperimentsRepo())
 
 
 def run_di():
     inject.configure(di_configuration)
+
+
+def clear_di():
+    inject.clear()
diff --git a/backend/app/tasks/tests.py b/backend/app/tasks/tests.py
index bc1f79384336608d774715885910873c27f7e2a7..28ccb7172077781577a23f9c22adc33cfc91dad1 100644
--- a/backend/app/tasks/tests.py
+++ b/backend/app/tasks/tests.py
@@ -10,4 +10,4 @@ def unit(c):
 @task
 def cov(c):
     """Run the unit tests and the test coverage."""
-    c.run("pytest --cov-report term --cov=app/ tests/")
+    c.run("pytest --cov-report term-missing --cov=app/ tests/")
diff --git a/backend/app/tests/api/endpoints/base_api_test.py b/backend/app/tests/api/endpoints/base_api_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a83782f5e6c26c9c3a631cbf6377af0301499de
--- /dev/null
+++ b/backend/app/tests/api/endpoints/base_api_test.py
@@ -0,0 +1,54 @@
+import logging
+import unittest
+
+from sqlmodel import create_engine, Session, SQLModel
+from sqlmodel.pool import StaticPool
+from fastapi.testclient import TestClient
+
+from app.db import get_session
+from app.dependency_injections import run_di, clear_di
+from app.main import app
+
+logging.basicConfig()
+logger = logging.getLogger()
+
+
+class BaseApiTests(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls) -> None:
+        # Dependency injections
+        run_di()
+
+        cls.engine = create_engine(
+            "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool
+        )
+        SQLModel.metadata.create_all(cls.engine)
+        with Session(cls.engine) as session:
+            cls._create_test_db(session)
+
+    @classmethod
+    def _create_test_db(cls, session: Session):
+        pass
+
+    def setUp(self) -> None:
+        self.session = Session(self.engine)
+
+        def get_session_override():
+            return self.session
+
+        app.dependency_overrides[get_session] = get_session_override
+        self.client = TestClient(app)
+
+    def tearDown(self) -> None:
+        app.dependency_overrides.clear()
+        self.session.close()
+
+    @classmethod
+    def tearDownClass(cls) -> None:
+        clear_di()
+
+    def _delete_entry(self, entry):
+        self.session.rollback()
+        logger.info(f"Deleting {entry}...")
+        self.session.delete(entry)
+        self.session.commit()
diff --git a/backend/app/tests/api/endpoints/test_catalogs.py b/backend/app/tests/api/endpoints/test_catalogs.py
index 4bb7f4cf61b37d48114d19f21ef90a64713851b4..51279c207b1c22ac9fbf174dc0b16647ffd45205 100644
--- a/backend/app/tests/api/endpoints/test_catalogs.py
+++ b/backend/app/tests/api/endpoints/test_catalogs.py
@@ -1,46 +1,16 @@
-import unittest
+from sqlmodel import Session, select
 
-from sqlmodel import create_engine, Session, SQLModel, select
-from sqlmodel.pool import StaticPool
-from fastapi.testclient import TestClient
-
-from app.db import get_session
-from app.dependency_injections import run_di
-from app.main import app
 from app.core.models.catalog import Catalog
 
-
-def create_test_db(session: Session):
-    test_catalog = Catalog(name="test-catalog")
-    session.add(test_catalog)
-    session.commit()
+from .base_api_test import BaseApiTests
 
 
-class TestCatalogs(unittest.TestCase):
+class TestCatalogs(BaseApiTests):
     @classmethod
-    def setUpClass(cls) -> None:
-        # Dependency injections
-        run_di()
-
-        cls.engine = create_engine(
-            "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool
-        )
-        SQLModel.metadata.create_all(cls.engine)
-        with Session(cls.engine) as session:
-            create_test_db(session)
-
-    def setUp(self) -> None:
-        self.session = Session(self.engine)
-
-        def get_session_override():
-            return self.session
-
-        app.dependency_overrides[get_session] = get_session_override
-        self.client = TestClient(app)
-
-    def tearDown(self) -> None:
-        app.dependency_overrides.clear()
-        self.session.close()
+    def _create_test_db(cls, session: Session):
+        test_catalog = Catalog(name="test-catalog")
+        session.add(test_catalog)
+        session.commit()
 
     def test_get_catalog_non_existing(self):
         # When
@@ -72,15 +42,17 @@ class TestCatalogs(unittest.TestCase):
         cat_name = "created_catalog"
         json_input = {"name": cat_name}
         expected_data = {"name": cat_name, "doi": None}
-        # When
-        response = self.client.post("/api/catalogs/", json=json_input)
-        # Then
-        self.assertEqual(response.status_code, 200)
-        self.assertDictEqual(response.json(), expected_data)
-        # Finally delete item
-        cat = self.session.exec(select(Catalog).where(Catalog.name == cat_name)).one()
-        self.session.delete(cat)
-        self.session.commit()
+        try:
+            # When
+            response = self.client.post("/api/catalogs/", json=json_input)
+            # Then
+            self.assertEqual(response.status_code, 200)
+            self.assertDictEqual(response.json(), expected_data)
+        finally:
+            cat = self.session.exec(
+                select(Catalog).where(Catalog.name == cat_name)
+            ).one()
+            self._delete_entry(cat)
 
     def test_create_existing_catalog(self):
         # Given
@@ -125,11 +97,11 @@ class TestCatalogs(unittest.TestCase):
         self.session.commit()
         json_input = {"name": name_to_update, "doi": "12345"}
         expected_data = json_input
-        # When
-        response = self.client.put(f"/api/catalogs/", json=json_input)
-        # Then
-        self.assertEqual(response.status_code, 200)
-        self.assertDictEqual(response.json(), expected_data)
-        # Finally
-        self.session.delete(catalog_to_update)
-        self.session.commit()
+        try:
+            # When
+            response = self.client.put(f"/api/catalogs/", json=json_input)
+            # Then
+            self.assertEqual(response.status_code, 200)
+            self.assertDictEqual(response.json(), expected_data)
+        finally:
+            self._delete_entry(catalog_to_update)
diff --git a/backend/app/tests/api/endpoints/test_eggnogs.py b/backend/app/tests/api/endpoints/test_eggnogs.py
new file mode 100644
index 0000000000000000000000000000000000000000..52ac9b964783e0e8f4abd399ccdbfbaec3966667
--- /dev/null
+++ b/backend/app/tests/api/endpoints/test_eggnogs.py
@@ -0,0 +1,155 @@
+from sqlmodel import Session, select
+
+from app.core.models.eggnog import Eggnog
+
+from .base_api_test import BaseApiTests
+
+
+class TestEggnogs(BaseApiTests):
+    @classmethod
+    def _create_test_db(cls, session: Session):
+        test_eggnog = Eggnog(eggnog_id="COG1", name="test-eggnog", version="5.0")
+        session.add(test_eggnog)
+        session.commit()
+
+    def test_get_eggnog_non_existing(self):
+        # When
+        response = self.client.get("/api/eggnog/non-existing")
+        # Then
+        self.assertEqual(response.status_code, 404)
+
+    def test_get_eggnog_existing(self):
+        # Given
+        expected_id = "COG1"
+        expected_data = {
+            "eggnog_id": expected_id,
+            "name": "test-eggnog",
+            "version": "5.0",
+        }
+        # When
+        response = self.client.get(f"/api/eggnog/{expected_id}")
+        # Then
+        self.assertEqual(response.status_code, 200)
+        self.assertDictEqual(response.json(), expected_data)
+
+    def test_get_all_eggnogs(self):
+        # Given
+        expected_data = [{"eggnog_id": "COG1", "name": "test-eggnog", "version": "5.0"}]
+        # When
+        response = self.client.get(f"/api/eggnog/")
+        # Then
+        self.assertEqual(response.status_code, 200)
+        self.assertListEqual(response.json(), expected_data)
+
+    def test_create_eggnog(self):
+        # Given
+        eggnog_id = "COG9"
+        eggnog_name = "created_eggnog"
+        json_input = {"eggnog_id": eggnog_id, "name": eggnog_name, "version": "5.0"}
+        expected_data = {"eggnog_id": eggnog_id, "name": eggnog_name, "version": "5.0"}
+        try:
+            # When
+            response = self.client.post("/api/eggnog/", json=json_input)
+            # Then
+            self.assertEqual(response.status_code, 200)
+            self.assertDictEqual(response.json(), expected_data)
+        finally:
+            eggnog = self.session.exec(
+                select(Eggnog).where(Eggnog.eggnog_id == eggnog_id)
+            ).one()
+            self._delete_entry(eggnog)
+
+    def test_create_eggnog_invalid_version(self):
+        # Given
+        eggnog_id = "COG9"
+        eggnog_name = "created_eggnog"
+        json_input = {"eggnog_id": eggnog_id, "name": eggnog_name, "version": "8.0"}
+        # When
+        response = self.client.post("/api/eggnog/", json=json_input)
+        # Then
+        self.assertEqual(response.status_code, 422)
+
+    def test_create_existing_eggnog(self):
+        """This test should fail and need to be dealt with in issue#15"""
+        # # Given
+        # eggnog_id = "COG1"
+        # eggnog_name = "test-eggnog"
+        # json_input = {"eggnog_id": eggnog_id, "name": eggnog_name, 'version':"5.0"}
+        # # When
+        # response = self.client.post("/api/eggnog/", json=json_input)
+        # # Then
+        # self.assertEqual(response.status_code, 403)
+
+    def test_delete_eggnog_non_existing(self):
+        # When
+        response = self.client.delete(f"/api/eggnog/non-existing")
+        # Then
+        self.assertEqual(response.status_code, 404)
+
+    def test_delete_eggnog(self):
+        # Given
+        eggnog_id = "COG5"
+        name_to_delete = "delete-me"
+        # - create eggnog to delete
+        eggnog_to_delete = Eggnog(
+            eggnog_id=eggnog_id, name=name_to_delete, version="5.0"
+        )
+        self.session.add(eggnog_to_delete)
+        self.session.commit()
+        # When
+        response = self.client.delete(f"/api/eggnog/{eggnog_id}")
+        # Then
+        self.assertEqual(response.status_code, 200)
+
+    def test_update_eggnog_non_existing(self):
+        # When
+        json_input = {"eggnog_id": "i-dont-exist", "name": "12345", "version": "5.0"}
+        response = self.client.put(f"/api/eggnog/", json=json_input)
+        # Then
+        self.assertEqual(response.status_code, 404)
+
+    def test_update_eggnog_no_version_exclude_none_true(self):
+        # Given
+        eggnog_id = "COG2"
+        name_to_update = "update-me"
+        new_name = "new-name"
+        # - create eggnog to update
+        eggnog_to_update = Eggnog(
+            eggnog_id=eggnog_id, name=name_to_update, version="5.0"
+        )
+        self.session.add(eggnog_to_update)
+        self.session.commit()
+        json_input = {"eggnog_id": eggnog_id, "name": new_name}
+        expected_data = {"eggnog_id": eggnog_id, "name": new_name, "version": "5.0"}
+        try:
+            # When
+            response = self.client.put(
+                f"/api/eggnog/?exclude_none=true", json=json_input
+            )
+            # Then
+            self.assertEqual(response.status_code, 200)
+            self.assertDictEqual(response.json(), expected_data)
+        finally:
+            self._delete_entry(eggnog_to_update)
+
+    def test_update_eggnog_no_version_exclude_none_false(self):
+        # Given
+        eggnog_id = "COG2"
+        name_to_update = "update-me"
+        new_name = "new-name"
+        # - create eggnog to update
+        eggnog_to_update = Eggnog(
+            eggnog_id=eggnog_id, name=name_to_update, version="5.0"
+        )
+        self.session.add(eggnog_to_update)
+        self.session.commit()
+        json_input = {"eggnog_id": eggnog_id, "name": new_name}
+        try:
+            # When
+            response = self.client.put(
+                f"/api/eggnog/?exclude_none=false", json=json_input
+            )
+            # Then
+            self.assertEqual(response.status_code, 422)
+        finally:
+            self._delete_entry(eggnog_to_update)
diff --git a/backend/app/tests/api/endpoints/test_experiments.py b/backend/app/tests/api/endpoints/test_experiments.py
new file mode 100644
index 0000000000000000000000000000000000000000..45ce4fb9ae72b7b68b0f03e340d7006685461499
--- /dev/null
+++ b/backend/app/tests/api/endpoints/test_experiments.py
@@ -0,0 +1,185 @@
+from sqlmodel import Session, select
+
+from app.core.models.experiment import Experiment
+
+from .base_api_test import BaseApiTests
+
+
+class TestExperiments(BaseApiTests):
+    @classmethod
+    def _create_test_db(cls, session: Session):
+        test_experiment = Experiment(name="test-experiment", date="2020-10-10")
+        session.add(test_experiment)
+        session.commit()
+        session.refresh(test_experiment)
+        cls.experiment_id_existing = test_experiment.id
+
+    def test_get_experiment_non_existing(self):
+        # When
+        response = self.client.get("/api/experiments/654")
+        # Then
+        self.assertEqual(response.status_code, 404)
+
+    def test_get_experiment_existing(self):
+        # Given
+        expected_id = self.experiment_id_existing
+        expected_data = {
+            "id": expected_id,
+            "name": "test-experiment",
+            "date": "2020-10-10",
+            "description": None,
+            "doi": None,
+        }
+        # When
+        response = self.client.get(f"/api/experiments/{expected_id}")
+        # Then
+        self.assertEqual(response.status_code, 200)
+        self.assertDictEqual(response.json(), expected_data)
+
+    def test_get_all_experiments(self):
+        # Given
+        expected_data = [
+            {
+                "id": 1,
+                "name": "test-experiment",
+                "date": "2020-10-10",
+                "description": None,
+                "doi": None,
+            }
+        ]
+        # When
+        response = self.client.get(f"/api/experiments/")
+        # Then
+        self.assertEqual(response.status_code, 200)
+        self.assertListEqual(response.json(), expected_data)
+
+    def test_create_experiment(self):
+        # Given
+        experiment_name = "created_experiment"
+        json_input = {
+            "name": experiment_name,
+            "date": "2021-11-11",
+        }
+        expected_data = {
+            "name": experiment_name,
+            "date": "2021-11-11",
+            "description": None,
+            "doi": None,
+        }
+        created_id = None
+        try:
+            # When
+            response = self.client.post("/api/experiments/", json=json_input)
+            # Then
+            self.assertEqual(response.status_code, 200)
+            for k, v in response.json().items():
+                if k == "id":
+                    created_id = v
+                else:
+                    self.assertEqual(v, expected_data[k])
+        finally:
+            try:
+                experiment = self.session.exec(
+                    select(Experiment).where(Experiment.id == created_id)
+                ).one()
+                self._delete_entry(experiment)
+            except:
+                pass
+
+    def test_create_experiment_invalid_date(self):
+        # Given
+        json_input = {
+            "name": "invalid-date_experiment",
+            "date": "20-2",
+        }
+        # When
+        response = self.client.post("/api/experiments/", json=json_input)
+        # Then
+        self.assertEqual(response.status_code, 422)
+
+    def test_delete_experiment_non_existing(self):
+        # When
+        response = self.client.delete(f"/api/experiments/123456")
+        # Then
+        self.assertEqual(response.status_code, 404)
+
+    def test_delete_experiment(self):
+        # Given
+        name_to_delete = "delete-me"
+        # - create experiment to delete
+        experiment_to_delete = Experiment(name=name_to_delete, date="2010-10-10")
+        self.session.add(experiment_to_delete)
+        self.session.commit()
+        self.session.refresh(experiment_to_delete)
+        # When
+        response = self.client.delete(f"/api/experiments/{experiment_to_delete.id}")
+        # Then
+        self.assertEqual(response.status_code, 200)
+
+    def test_update_experiment_non_existing(self):
+        # When
+        json_input = {
+            "id": 123456,
+            "name": "never-updated-experiment",
+        }
+        response = self.client.put(f"/api/experiments/", json=json_input)
+        # Then
+        self.assertEqual(response.status_code, 404)
+
+    def test_update_experiment_no_date_exclude_none_true(self):
+        # Given
+        name_to_update = "update-me"
+        new_name = "new-name"
+        new_description = "this experiment was difficult"
+        # - create experiment to update
+        experiment_to_update = Experiment(name=name_to_update, date="2021-10-10")
+        self.session.add(experiment_to_update)
+        self.session.commit()
+        self.session.refresh(experiment_to_update)
+        json_input = {
+            "id": experiment_to_update.id,
+            "name": new_name,
+            "description": new_description,
+        }
+        expected_data = {
+            "id": experiment_to_update.id,
+            "name": new_name,
+            "description": new_description,
+            "date": "2021-10-10",
+            "doi": None,
+        }
+        try:
+            # When
+            response = self.client.put(
+                f"/api/experiments/?exclude_none=true", json=json_input
+            )
+            # Then
+            self.assertEqual(response.status_code, 200)
+            self.assertDictEqual(response.json(), expected_data)
+        finally:
+            self._delete_entry(experiment_to_update)
+
+    def test_update_experiment_no_date_exclude_none_false(self):
+        # Given
+        name_to_update = "update-me"
+        new_name = "new-name"
+        new_description = "this experiment was difficult"
+        # - create experiment to update
+        experiment_to_update = Experiment(name=name_to_update, date="2021-10-10")
+        self.session.add(experiment_to_update)
+        self.session.commit()
+        self.session.refresh(experiment_to_update)
+        json_input = {
+            "id": experiment_to_update.id,
+            "name": new_name,
+            "description": new_description,
+        }
+        try:
+            # When
+            response = self.client.put(
+                f"/api/experiments/?exclude_none=false", json=json_input
+            )
+            # Then
+            self.assertEqual(response.status_code, 422)
+        finally:
+            self._delete_entry(experiment_to_update)
diff --git a/backend/app/tests/api/endpoints/test_keggs.py b/backend/app/tests/api/endpoints/test_keggs.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee407cc7eddbec8a6db71696490113aa73e0a75c
--- /dev/null
+++ b/backend/app/tests/api/endpoints/test_keggs.py
@@ -0,0 +1,127 @@
+from sqlmodel import Session, select
+
+from app.core.models.kegg import KeggOrthology
+
+from .base_api_test import BaseApiTests
+
+
+class TestKeggOrthologys(BaseApiTests):
+    @classmethod
+    def _create_test_db(cls, session: Session):
+        test_kegg = KeggOrthology(kegg_id="k1", name="test-kegg")
+        session.add(test_kegg)
+        session.commit()
+
+    def test_get_kegg_non_existing(self):
+        # When
+        response = self.client.get("/api/kegg-orthology/non-existing")
+        # Then
+        self.assertEqual(response.status_code, 404)
+
+    def test_get_kegg_existing(self):
+        # Given
+        expected_id = "k1"
+        expected_data = {"kegg_id": expected_id, "name": "test-kegg"}
+        # When
+        response = self.client.get(f"/api/kegg-orthology/{expected_id}")
+        # Then
+        self.assertEqual(response.status_code, 200)
+        self.assertDictEqual(response.json(), expected_data)
+
+    def test_get_all_keggs(self):
+        # Given
+        expected_data = [{"kegg_id": "k1", "name": "test-kegg"}]
+        # When
+        response = self.client.get(f"/api/kegg-orthology/")
+        # Then
+        self.assertEqual(response.status_code, 200)
+        self.assertListEqual(response.json(), expected_data)
+
+    def test_create_kegg(self):
+        # Given
+        kegg_id = "k9"
+        kegg_name = "created_kegg"
+        json_input = {"kegg_id": kegg_id, "name": kegg_name}
+        expected_data = {"kegg_id": kegg_id, "name": kegg_name}
+        try:
+            # When
+            response = self.client.post("/api/kegg-orthology/", json=json_input)
+            # Then
+            self.assertEqual(response.status_code, 200)
+            self.assertDictEqual(response.json(), expected_data)
+        finally:
+            kegg = self.session.exec(select(KeggOrthology).where(KeggOrthology.kegg_id == kegg_id)).one()
+            self._delete_entry(kegg)
+
+    def test_create_existing_kegg(self):
+        # Given
+        kegg_id = "k1"
+        kegg_name = "test-kegg"
+        json_input = {"kegg_id": kegg_id, "name": kegg_name}
+        # When
+        response = self.client.post("/api/kegg-orthology/", json=json_input)
+        # Then
+        self.assertEqual(response.status_code, 403)
+
+    def test_delete_kegg_non_existing(self):
+        # When
+        response = self.client.delete(f"/api/kegg-orthology/non-existing")
+        # Then
+        self.assertEqual(response.status_code, 404)
+
+    def test_delete_kegg(self):
+        # Given
+        kegg_id = "k5"
+        name_to_delete = "delete-me"
+        # - create kegg to delete
+        kegg_to_delete = KeggOrthology(kegg_id=kegg_id, name=name_to_delete)
+        self.session.add(kegg_to_delete)
+        self.session.commit()
+        # When
+        response = self.client.delete(f"/api/kegg-orthology/{kegg_id}")
+        # Then
+        self.assertEqual(response.status_code, 200)
+
+    def test_update_kegg_non_existing(self):
+        # When
+        json_input = {"kegg_id": "i-dont-exist", "name": "12345"}
+        response = self.client.put(f"/api/kegg-orthology/", json=json_input)
+        # Then
+        self.assertEqual(response.status_code, 404)
+
+    def test_update_kegg(self):
+        # Given
+        kegg_id = "k2"
+        name_to_update = "update-me"
+        new_name = "new-name"
+        # - create kegg to update
+        kegg_to_update = KeggOrthology(kegg_id=kegg_id, name=name_to_update)
+        self.session.add(kegg_to_update)
+        self.session.commit()
+        json_input = {"kegg_id": kegg_id, "name": new_name}
+        expected_data = json_input
+        try:
+            # When
+            response = self.client.put(f"/api/kegg-orthology/", json=json_input)
+            # Then
+            self.assertEqual(response.status_code, 200)
+            self.assertDictEqual(response.json(), expected_data)
+        finally:
+            self._delete_entry(kegg_to_update)
+
+    def test_update_kegg_no_name_exclude_none_false(self):
+        # Given
+        kegg_id = "k2"
+        name_to_update = "update-me"
+        # - create kegg to update
+        kegg_to_update = KeggOrthology(kegg_id=kegg_id, name=name_to_update)
+        self.session.add(kegg_to_update)
+        self.session.commit()
+        json_input = {"kegg_id": kegg_id}
+        try:
+            # When
+            response = self.client.put(f"/api/kegg-orthology/?exclude_none=false", json=json_input)
+            # Then
+            self.assertEqual(response.status_code, 422)
+        finally:
+            self._delete_entry(kegg_to_update)
diff --git a/backend/app/tests/api/endpoints/test_ncbi_taxonomy.py b/backend/app/tests/api/endpoints/test_ncbi_taxonomy.py
new file mode 100644
index 0000000000000000000000000000000000000000..a0e7377e12bccbee88b550da0ef80ef3d47694e6
--- /dev/null
+++ b/backend/app/tests/api/endpoints/test_ncbi_taxonomy.py
@@ -0,0 +1,158 @@
+from sqlmodel import Session, select
+
+from app.core.models.ncbi_taxonomy import NcbiTaxonomy
+
+from .base_api_test import BaseApiTests
+
+
+class TestNcbiTaxonomys(BaseApiTests):
+    @classmethod
+    def _create_test_db(cls, session: Session):
+        test_ncbi_taxonomy = NcbiTaxonomy(
+            tax_id=1, name="test-ncbi_taxonomy", rank="species"
+        )
+        session.add(test_ncbi_taxonomy)
+        session.commit()
+
+    def test_get_ncbi_taxonomy_non_existing(self):
+        # When
+        response = self.client.get("/api/ncbi_taxonomy/non-existing")
+        # Then
+        self.assertEqual(response.status_code, 404)
+
+    def test_get_ncbi_taxonomy_existing(self):
+        # Given
+        expected_id = 1
+        expected_data = {
+            "tax_id": expected_id,
+            "name": "test-ncbi_taxonomy",
+            "rank": "species",
+        }
+        # When
+        response = self.client.get(f"/api/ncbi_taxonomy/{expected_id}")
+        # Then
+        self.assertEqual(response.status_code, 200)
+        self.assertDictEqual(response.json(), expected_data)
+
+    def test_get_all_ncbi_taxonomys(self):
+        # Given
+        expected_data = [{"tax_id": 1, "name": "test-ncbi_taxonomy", "rank": "species"}]
+        # When
+        response = self.client.get(f"/api/ncbi_taxonomy/")
+        # Then
+        self.assertEqual(response.status_code, 200)
+        self.assertListEqual(response.json(), expected_data)
+
+    def test_create_ncbi_taxonomy(self):
+        # Given
+        tax_id = 2
+        ncbi_taxonomy_name = "created_ncbi_taxonomy"
+        rank_created = "species"
+        json_input = {
+            "tax_id": tax_id,
+            "name": ncbi_taxonomy_name,
+            "rank": rank_created,
+        }
+        expected_data = {
+            "tax_id": tax_id,
+            "name": ncbi_taxonomy_name,
+            "rank": rank_created,
+        }
+        try:
+            # When
+            response = self.client.post("/api/ncbi_taxonomy/", json=json_input)
+            # Then
+            self.assertEqual(response.status_code, 200)
+            self.assertDictEqual(response.json(), expected_data)
+        finally:
+            ncbi_tax = self.session.exec(
+                select(NcbiTaxonomy).where(NcbiTaxonomy.tax_id == tax_id)
+            ).one()
+            self._delete_entry(ncbi_tax)
+
+    def test_create_existing_ncbi_taxonomy(self):
+        # Given
+        tax_id = 1
+        ncbi_taxonomy_name = "test-ncbi_taxonomy"
+        json_input = {"tax_id": tax_id, "name": ncbi_taxonomy_name, "rank": "species"}
+        # When
+        response = self.client.post("/api/ncbi_taxonomy/", json=json_input)
+        # Then
+        self.assertEqual(response.status_code, 403)
+
+    def test_delete_ncbi_taxonomy_non_existing(self):
+        # When
+        response = self.client.delete(f"/api/ncbi_taxonomy/35")
+        # Then
+        self.assertEqual(response.status_code, 404)
+
+    def test_delete_ncbi_taxonomy(self):
+        # Given
+        tax_id = 4
+        name_to_delete = "delete-me"
+        # - create ncbi_taxonomy to delete
+        ncbi_taxonomy_to_delete = NcbiTaxonomy(
+            tax_id=tax_id, name=name_to_delete, rank="species"
+        )
+        self.session.add(ncbi_taxonomy_to_delete)
+        self.session.commit()
+        # When
+        response = self.client.delete(f"/api/ncbi_taxonomy/{tax_id}")
+        # Then
+        self.assertEqual(response.status_code, 200)
+
+    def test_update_ncbi_taxonomy_non_existing_no_rank_exclude_none_true(self):
+        # When
+        json_input = {"tax_id": 29, "name": "12345"}
+        response = self.client.put(
+            f"/api/ncbi_taxonomy/?exclude_none=true", json=json_input
+        )
+        # Then
+        self.assertEqual(response.status_code, 404)
+
+    def test_update_ncbi_taxonomy_no_rank_exclude_none_true(self):
+        # Given
+        tax_id = 56
+        name_to_update = "update-me"
+        new_name = "new-name"
+        rank = "species"
+        # - create ncbi_taxonomy to update
+        ncbi_taxonomy_to_update = NcbiTaxonomy(
+            tax_id=tax_id, name=name_to_update, rank=rank
+        )
+        self.session.add(ncbi_taxonomy_to_update)
+        self.session.commit()
+        json_input = {"tax_id": tax_id, "name": new_name}
+        expected_data = {"tax_id": tax_id, "name": new_name, "rank": rank}
+        try:
+            # When
+            response = self.client.put(
+                f"/api/ncbi_taxonomy/?exclude_none=true", json=json_input
+            )
+            # Then
+            self.assertEqual(response.status_code, 200)
+            self.assertDictEqual(response.json(), expected_data)
+        finally:
+            self._delete_entry(ncbi_taxonomy_to_update)
+
+    def test_update_ncbi_taxonomy_no_rank_exclude_none_false(self):
+        # Given
+        tax_id = 56
+        name_to_update = "update-me"
+        new_name = "new-name"
+        rank = "species"
+        # - create ncbi_taxonomy to update
+        ncbi_taxonomy_to_update = NcbiTaxonomy(
+            tax_id=tax_id, name=name_to_update, rank=rank
+        )
+        self.session.add(ncbi_taxonomy_to_update)
+        self.session.commit()
+        json_input = {"tax_id": tax_id, "name": new_name}
+        try:
+            # When
+            response = self.client.put(
+                f"/api/ncbi_taxonomy/?exclude_none=false", json=json_input
+            )
+            self.assertEqual(response.status_code, 422)
+        finally:
+            self._delete_entry(ncbi_taxonomy_to_update)
diff --git a/backend/app/tests/core/use_cases/crud/test_eggnog.py b/backend/app/tests/core/use_cases/crud/test_eggnog.py
new file mode 100644
index 0000000000000000000000000000000000000000..2fdd980d9f46c67b5ef05824a94040a5e1c9e776
--- /dev/null
+++ b/backend/app/tests/core/use_cases/crud/test_eggnog.py
@@ -0,0 +1,76 @@
+import unittest
+
+from app.core.use_cases.crud.eggnog import CrudEggnogUseCase
+from app.core.schemas.entities.eggnog import EggnogCreate, EggnogUpdate
+
+GET_ACTION = "Returning eggnog"
+GET_ALL_ACTION = "Returning all eggnogs"
+CREATE_ACTION = "Creating eggnog"
+UPDATE_ACTION = "Updating eggnog"
+DELETE_ACTION = "Deleting eggnog"
+
+
+class MockEggnogsRepo:
+    def get(self, eggnog_id: str):
+        return GET_ACTION
+
+    def get_all(self):
+        return GET_ALL_ACTION
+
+    def create(self, eggnog_input: EggnogCreate):
+        return CREATE_ACTION
+
+    def update(self, eggnog_input: EggnogUpdate):
+        return UPDATE_ACTION
+
+    def delete(self, eggnog_id: str):
+        return DELETE_ACTION
+
+
+class TestCrudEggnogUseCase(unittest.TestCase):
+    def setUp(self) -> None:
+        self.use_case_test = CrudEggnogUseCase(eggnogs_repo=MockEggnogsRepo())
+
+    def test_create_eggnog(self):
+        # Given
+        eggnog_test = EggnogCreate(eggnog_id="COG01", name="test-eggnog", version="5.0")
+        expected_output = CREATE_ACTION
+        # When
+        test_output = self.use_case_test.create_eggnog(eggnog_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_update_eggnog(self):
+        # Given
+        eggnog_test = EggnogCreate(eggnog_id="COG01", name="test-eggnog", version="5.0")
+        expected_output = UPDATE_ACTION
+        # When
+        test_output = self.use_case_test.update_eggnog(eggnog_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_delete_eggnog(self):
+        # Given
+        eggnog_id_test = "test-eggnog"
+        expected_output = DELETE_ACTION
+        # When
+        test_output = self.use_case_test.delete_eggnog(eggnog_id_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_get_eggnog(self):
+        # Given
+        eggnog_id_test = "test-eggnog"
+        expected_output = GET_ACTION
+        # When
+        test_output = self.use_case_test.get_eggnog(eggnog_id_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_get_all(self):
+        # Given
+        expected_output = GET_ALL_ACTION
+        # When
+        output = self.use_case_test.get_all()
+        # Then
+        self.assertEqual(output, expected_output)
diff --git a/backend/app/tests/core/use_cases/crud/test_experiment.py b/backend/app/tests/core/use_cases/crud/test_experiment.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c4f6b50cd947c6e862da76a54dd23964baa45e0
--- /dev/null
+++ b/backend/app/tests/core/use_cases/crud/test_experiment.py
@@ -0,0 +1,78 @@
+import unittest
+
+from app.core.use_cases.crud.experiment import CrudExperimentUseCase
+from app.core.schemas.entities.experiment import ExperimentCreate, ExperimentUpdate
+
+GET_ACTION = "Returning experiment"
+GET_ALL_ACTION = "Returning all experiments"
+CREATE_ACTION = "Creating experiment"
+UPDATE_ACTION = "Updating experiment"
+DELETE_ACTION = "Deleting experiment"
+
+
+class MockExperimentsRepo:
+    def get(self, experiment_id: str):
+        return GET_ACTION
+
+    def get_all(self):
+        return GET_ALL_ACTION
+
+    def create(self, experiment_input: ExperimentCreate):
+        return CREATE_ACTION
+
+    def update(self, experiment_input: ExperimentUpdate):
+        return UPDATE_ACTION
+
+    def delete(self, experiment_id: str):
+        return DELETE_ACTION
+
+
+class TestCrudExperimentUseCase(unittest.TestCase):
+    def setUp(self) -> None:
+        self.use_case_test = CrudExperimentUseCase(
+            experiments_repo=MockExperimentsRepo()
+        )
+
+    def test_create_experiment(self):
+        # Given
+        experiment_test = ExperimentCreate(name="test-experiment", date="2020-10-10")
+        expected_output = CREATE_ACTION
+        # When
+        test_output = self.use_case_test.create_experiment(experiment_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_update_experiment(self):
+        # Given
+        experiment_test = ExperimentCreate(name="test-experiment", date="2020-10-10")
+        expected_output = UPDATE_ACTION
+        # When
+        test_output = self.use_case_test.update_experiment(experiment_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_delete_experiment(self):
+        # Given
+        experiment_id_test = "test-experiment"
+        expected_output = DELETE_ACTION
+        # When
+        test_output = self.use_case_test.delete_experiment(experiment_id_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_get_experiment(self):
+        # Given
+        experiment_id_test = "test-experiment"
+        expected_output = GET_ACTION
+        # When
+        test_output = self.use_case_test.get_experiment(experiment_id_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_get_all(self):
+        # Given
+        expected_output = GET_ALL_ACTION
+        # When
+        output = self.use_case_test.get_all()
+        # Then
+        self.assertEqual(output, expected_output)
diff --git a/backend/app/tests/core/use_cases/crud/test_kegg.py b/backend/app/tests/core/use_cases/crud/test_kegg.py
new file mode 100644
index 0000000000000000000000000000000000000000..c68feb70ce5dbf66ff379ea7c76010493858281f
--- /dev/null
+++ b/backend/app/tests/core/use_cases/crud/test_kegg.py
@@ -0,0 +1,76 @@
+import unittest
+
+from app.core.use_cases.crud.kegg import CrudKeggOrthologyUseCase
+from app.core.schemas.entities.kegg import KeggOrthologyCreate, KeggOrthologyUpdate
+
+GET_ACTION = "Returning kegg"
+GET_ALL_ACTION = "Returning all keggs"
+CREATE_ACTION = "Creating kegg"
+UPDATE_ACTION = "Updating kegg"
+DELETE_ACTION = "Deleting kegg"
+
+
+class MockKeggOrthologysRepo:
+    def get(self, kegg_id: str):
+        return GET_ACTION
+
+    def get_all(self):
+        return GET_ALL_ACTION
+
+    def create(self, kegg_input: KeggOrthologyCreate):
+        return CREATE_ACTION
+
+    def update(self, kegg_input: KeggOrthologyUpdate):
+        return UPDATE_ACTION
+
+    def delete(self, kegg_id: str):
+        return DELETE_ACTION
+
+
+class TestCrudKeggOrthologyUseCase(unittest.TestCase):
+    def setUp(self) -> None:
+        self.use_case_test = CrudKeggOrthologyUseCase(keggs_repo=MockKeggOrthologysRepo())
+
+    def test_create_kegg(self):
+        # Given
+        kegg_test = KeggOrthologyCreate(kegg_id="k01", name="test-kegg")
+        expected_output = CREATE_ACTION
+        # When
+        test_output = self.use_case_test.create_kegg(kegg_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_update_kegg(self):
+        # Given
+        kegg_test = KeggOrthologyCreate(kegg_id="k01", name="test-kegg")
+        expected_output = UPDATE_ACTION
+        # When
+        test_output = self.use_case_test.update_kegg(kegg_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_delete_kegg(self):
+        # Given
+        kegg_id_test = "test-kegg"
+        expected_output = DELETE_ACTION
+        # When
+        test_output = self.use_case_test.delete_kegg(kegg_id_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_get_kegg(self):
+        # Given
+        kegg_id_test = "test-kegg"
+        expected_output = GET_ACTION
+        # When
+        test_output = self.use_case_test.get_kegg(kegg_id_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_get_all(self):
+        # Given
+        expected_output = GET_ALL_ACTION
+        # When
+        output = self.use_case_test.get_all()
+        # Then
+        self.assertEqual(output, expected_output)
diff --git a/backend/app/tests/core/use_cases/crud/test_ncbi_taxonomy.py b/backend/app/tests/core/use_cases/crud/test_ncbi_taxonomy.py
new file mode 100644
index 0000000000000000000000000000000000000000..a72c333b1dd0e9fced69eea2247250b4354efce0
--- /dev/null
+++ b/backend/app/tests/core/use_cases/crud/test_ncbi_taxonomy.py
@@ -0,0 +1,85 @@
+import unittest
+
+from app.core.use_cases.crud.ncbi_taxonomy import CrudNcbiTaxonomyUseCase
+from app.core.schemas.entities.ncbi_taxonomy import (
+    NcbiTaxonomyCreate,
+    NcbiTaxonomyUpdate,
+)
+
+GET_ACTION = "Returning ncbi_taxonomy"
+GET_ALL_ACTION = "Returning all ncbi_taxonomy entries"
+CREATE_ACTION = "Creating ncbi_taxonomy"
+UPDATE_ACTION = "Updating ncbi_taxonomy"
+DELETE_ACTION = "Deleting ncbi_taxonomy"
+
+
+class MockNcbiTaxonomysRepo:
+    def get(self, ncbi_taxonomy_name: str):
+        return GET_ACTION
+
+    def get_all(self):
+        return GET_ALL_ACTION
+
+    def create(self, ncbi_taxonomy_input: NcbiTaxonomyCreate):
+        return CREATE_ACTION
+
+    def update(self, ncbi_taxonomy_input: NcbiTaxonomyUpdate):
+        return UPDATE_ACTION
+
+    def delete(self, ncbi_taxonomy_name: str):
+        return DELETE_ACTION
+
+
+class TestCrudNcbiTaxonomyUseCase(unittest.TestCase):
+    def setUp(self) -> None:
+        self.use_case_test = CrudNcbiTaxonomyUseCase(
+            ncbi_taxonomys_repo=MockNcbiTaxonomysRepo()
+        )
+
+    def test_create_ncbi_taxonomy(self):
+        # Given
+        ncbi_taxonomy_test = NcbiTaxonomyCreate(
+            tax_id=1, rank="species", name="test-ncbi_taxonomy"
+        )
+        expected_output = CREATE_ACTION
+        # When
+        test_output = self.use_case_test.create_ncbi_taxonomy(ncbi_taxonomy_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_update_ncbi_taxonomy(self):
+        # Given
+        ncbi_taxonomy_test = NcbiTaxonomyCreate(
+            tax_id=1, rank="species", name="test-ncbi_taxonomy"
+        )
+        expected_output = UPDATE_ACTION
+        # When
+        test_output = self.use_case_test.update_ncbi_taxonomy(ncbi_taxonomy_test)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_delete_ncbi_taxonomy(self):
+        # Given
+        ncbi_taxonomy_test_name = "test-ncbi_taxonomy"
+        expected_output = DELETE_ACTION
+        # When
+        test_output = self.use_case_test.delete_ncbi_taxonomy(ncbi_taxonomy_test_name)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_get_ncbi_taxonomy(self):
+        # Given
+        ncbi_taxonomy_test_name = "test-ncbi_taxonomy"
+        expected_output = GET_ACTION
+        # When
+        test_output = self.use_case_test.get_ncbi_taxonomy(ncbi_taxonomy_test_name)
+        # Then
+        self.assertEqual(test_output, expected_output)
+
+    def test_get_all(self):
+        # Given
+        expected_output = GET_ALL_ACTION
+        # When
+        output = self.use_case_test.get_all()
+        # Then
+        self.assertEqual(output, expected_output)