diff --git a/client/src/app/admin/containers/attribute/attribute-list.component.html b/client/src/app/admin/containers/attribute/attribute-list.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..f476ec3ad5cde560e6ef0ca194c606787ce4cfef
--- /dev/null
+++ b/client/src/app/admin/containers/attribute/attribute-list.component.html
@@ -0,0 +1,49 @@
+<div class="container-fluid">
+    <nav aria-label="breadcrumb">
+        <ol class="breadcrumb">
+            <li class="breadcrumb-item">
+                <a routerLink="/instance-list">Instances</a>
+            </li>
+            <li class="breadcrumb-item">
+                <a routerLink="/configure-instance/{{ instanceSelected | async }}">
+                    Configure instance {{ instanceSelected | async }}
+                </a>
+            </li>
+            <li class="breadcrumb-item active" aria-current="page">
+                Configure dataset {{ datasetSelected | async }}
+            </li>
+        </ol>
+    </nav>
+
+    <app-spinner *ngIf="(attributeListIsLoading | async) || (datasetListIsLoading | async)"></app-spinner>
+
+    <div *ngIf="(attributeListIsLoaded | async) && (datasetListIsLoaded | async)" class="row mt-1">
+        <div class="col-12">
+            <app-form-attribute-list 
+                [dataset]="dataset | async"
+                [attributeList]="attributeList | async"
+                [columnList]="columnList | async"
+                [optionListGenerated]="optionListGenerated | async"
+                [criteriaFamilyList]="criteriaFamilyList | async"
+                [outputFamilyList]="outputFamilyList | async" 
+                [outputCategoryList]="outputCategoryList | async"
+                [settingsSelectList]="settingsSelectList | async"
+                [settingsSelectOptionList]="settingsSelectOptionList | async"
+                [tabSelected]="tabSelected | async"
+                (addCriteriaFamily)="addCriteriaFamily($event)"
+                (editCriteriaFamily)="editCriteriaFamily($event)"
+                (deleteCriteriaFamily)="deleteCriteriaFamily($event)"
+                (addOutputFamily)="addOutputFamily($event)"
+                (editOutputFamily)="editOutputFamily($event)"
+                (deleteOutputFamily)="deleteOutputFamily($event)"
+                (addAttribute)="addAttribute($event)"
+                (editAttribute)="editAttribute($event)"
+                (deleteAttribute)="deleteAttribute($event)"
+                (addOutputCategory)="addOutputCategory($event)"
+                (editOutputCategory)="editOutputCategory($event)"
+                (deleteOutputCategory)="deleteOutputCategory($event)"
+                (generateAttributeOptionList)="generateAttributeOptionList($event)">
+            </app-form-attribute-list>
+        </div>
+    </div>
+</div>
diff --git a/client/src/app/admin/containers/attribute/attribute-list.component.scss b/client/src/app/admin/containers/attribute/attribute-list.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..6b917133e64d49ce00365302ae03f1e02101b4cc
--- /dev/null
+++ b/client/src/app/admin/containers/attribute/attribute-list.component.scss
@@ -0,0 +1,18 @@
+.attributes {
+    margin-right: 20px;
+    margin-left: 20px;
+    padding-top: 15px;
+    padding-bottom: 15px;
+    border-top: 1px solid;
+    border-color: #ccc;
+}
+
+th {
+    background-color: #a8c96e;
+    color: whitesmoke;
+    text-align: center;
+}
+
+.td-att-name {
+    background-color: #dcdada;
+}
diff --git a/client/src/app/admin/containers/attribute/attribute-list.component.ts b/client/src/app/admin/containers/attribute/attribute-list.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0d9315262e237fa0323834244050feb5c78507b8
--- /dev/null
+++ b/client/src/app/admin/containers/attribute/attribute-list.component.ts
@@ -0,0 +1,136 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { Store } from '@ngrx/store';
+
+import { Select, SelectOption, Dataset, Attribute, Column, CriteriaFamily, OutputCategory, OutputFamily } from 'src/app/metamodel/store/models';
+import * as instanceSelector from 'src/app/metamodel/store/selectors/instance.selector';
+import * as datasetActions from 'src/app/metamodel/store/actions/dataset.actions';
+import * as datasetSelector from 'src/app/metamodel/store/selectors/dataset.selector';
+import * as attributeActions from 'src/app/metamodel/store/actions/attribute.actions';
+import * as attributeSelector from 'src/app/metamodel/store/selectors/attribute.selector';
+import * as criteriaFamilyActions from 'src/app/metamodel/store/actions/criteria-family.actions';
+import * as criteriaFamilySelector from 'src/app/metamodel/store/selectors/criteria-family.selector';
+import * as outputFamilyActions from 'src/app/metamodel/store/actions/output-family.actions';
+import * as outputFamilySelector from 'src/app/metamodel/store/selectors/output-family.selector';
+import * as outputCategoryActions from 'src/app/metamodel/store/actions/output-category.actions';
+import * as outputCategorySelector from 'src/app/metamodel/store/selectors/output-category.selector';
+import * as selectActions from 'src/app/metamodel/store/actions/select.actions';
+import * as selectSelector from 'src/app/metamodel/store/selectors/select.selector';
+import * as optionActions from 'src/app/metamodel/store/actions/select-option.actions';
+import * as optionSelector from 'src/app/metamodel/store/selectors/select-option.selector';
+import * as columnActions from 'src/app/metamodel/store/actions/column.actions';
+import * as columnSelector from 'src/app/metamodel/store/selectors/column.selector';
+
+@Component({
+    selector: 'app-attribute',
+    templateUrl: 'attribute-list.component.html',
+    styleUrls: [ 'attribute-list.component.scss' ]
+})
+export class AttributeComponent implements OnInit {
+    public instanceName: Observable<string>;
+    public datasetName: Observable<string>;
+    public dataset: Observable<Dataset>;
+    public datasetListIsLoading: Observable<boolean>;
+    public datasetListIsLoaded: Observable<boolean>;
+    public tabSelected: Observable<string>;
+    public attributeList: Observable<Attribute[]>;
+    public attributeListIsLoading: Observable<boolean>;
+    public attributeListIsLoaded: Observable<boolean>;
+    public columnList: Observable<Column[]>;
+    public columnListIsLoading: Observable<boolean>;
+    public columnListIsLoaded: Observable<boolean>;
+    public optionListGenerated: Observable<string[]>;
+    public criteriaFamilyList: Observable<CriteriaFamily[]>;
+    public outputFamilyList: Observable<OutputFamily[]>;
+    public outputCategoryList: Observable<OutputCategory[]>;
+    public settingsSelectList: Observable<Select[]>;
+    public settingsSelectOptionList: Observable<SelectOption[]>;
+
+    constructor(private store: Store<{ }>, private route: ActivatedRoute) {
+        this.instanceName = store.select(instanceSelector.selectInstanceNameByRoute);
+        this.datasetName = store.select(datasetSelector.selectDatasetNameByRoute);
+        this.dataset = store.select(datasetSelector.selectDatasetByRouteName);
+        this.datasetListIsLoading = store.select(datasetSelector.selectDatasetListIsLoading);
+        this.datasetListIsLoaded = store.select(datasetSelector.selectDatasetListIsLoaded);
+        this.attributeList = store.select(attributeSelector.selectAllAttributes);
+        this.attributeListIsLoading = store.select(attributeSelector.selectAttributeListIsLoading);
+        this.attributeListIsLoaded = store.select(attributeSelector.selectAttributeListIsLoaded);
+        this.columnList = store.select(columnSelector.selectAllColumns);
+        this.columnListIsLoading = store.select(columnSelector.selectColumnListIsLoading);
+        this.columnListIsLoaded = store.select(columnSelector.selectColumnListIsLoaded);
+        this.optionListGenerated = store.select(attributeSelector.getOptionListGenerated);
+        this.criteriaFamilyList = store.select(criteriaFamilySelector.selectAllCriteriaFamilys);
+        this.outputFamilyList = store.select(outputFamilySelector.selectAllOutputFamilys);
+        this.outputCategoryList = store.select(outputCategorySelector.selectAllOutputCategorys);
+        this.settingsSelectList = store.select(selectSelector.selectAllSelects);
+        this.settingsSelectOptionList = store.select(optionSelector.selectAllSelectOptions);
+    }
+
+    ngOnInit() {
+        this.store.dispatch(datasetActions.loadDatasetList());
+        this.store.dispatch(attributeActions.loadAttributeList());
+        this.store.dispatch(selectActions.loadSelectList());
+        this.store.dispatch(optionActions.loadSelectOptionList());
+        this.store.dispatch(criteriaFamilyActions.loadCriteriaFamilyList());
+        this.store.dispatch(outputFamilyActions.loadOutputFamilyList());
+        this.store.dispatch(outputCategoryActions.loadOutputCategoryList());
+        this.tabSelected = this.route.queryParamMap.pipe(
+            map(params => params.get('tab_selected'))
+        );
+    }
+
+    addCriteriaFamily(criteriaFamily: CriteriaFamily) {
+        this.store.dispatch(criteriaFamilyActions.addCriteriaFamily({ criteriaFamily }));
+    }
+
+    editCriteriaFamily(criteriaFamily: CriteriaFamily) {
+        this.store.dispatch(criteriaFamilyActions.editCriteriaFamily({ criteriaFamily }));
+    }
+
+    deleteCriteriaFamily(criteriaFamily: CriteriaFamily) {
+        this.store.dispatch(criteriaFamilyActions.deleteCriteriaFamily({ criteriaFamily }));
+    }
+
+    addOutputFamily(outputFamily: OutputFamily) {
+        this.store.dispatch(outputFamilyActions.addOutputFamily({ outputFamily }));
+    }
+
+    editOutputFamily(outputFamily: OutputFamily) {
+        this.store.dispatch(outputFamilyActions.editOutputFamily({ outputFamily }));
+    }
+
+    deleteOutputFamily(outputFamily: OutputFamily) {
+        this.store.dispatch(outputFamilyActions.deleteOutputFamily({ outputFamily }));
+    }
+
+    addOutputCategory(outputCategory: OutputCategory) {
+        this.store.dispatch(outputCategoryActions.addOutputCategory({ outputCategory }));
+    }
+
+    editOutputCategory(outputCategory: OutputCategory) {
+        this.store.dispatch(outputCategoryActions.editOutputCategory({ outputCategory }));
+    }
+
+    deleteOutputCategory(outputCategory: OutputCategory) {
+        this.store.dispatch(outputCategoryActions.deleteOutputCategory({ outputCategory }));
+    }
+
+    addAttribute(attribute: Attribute) {
+        this.store.dispatch(attributeActions.addAttribute({ attribute }));
+    }
+
+    editAttribute(attribute: Attribute) {
+        this.store.dispatch(attributeActions.editAttribute({ attribute }));
+    }
+
+    deleteAttribute(attribute: Attribute) {
+        this.store.dispatch(attributeActions.deleteAttribute({ attribute }));
+    }
+
+    generateAttributeOptionList(attribute: Attribute) {
+        this.store.dispatch(new attributeActions.GenerateOptionListAction(attribute));
+    }
+}
diff --git a/client/src/app/metamodel/store/actions/attribute.actions.ts b/client/src/app/metamodel/store/actions/attribute.actions.ts
index e26b4b230bd8c58a6c6be0478679d20357158b3d..01012dcb53eaa97012dbc0523b2720db0749ef6a 100644
--- a/client/src/app/metamodel/store/actions/attribute.actions.ts
+++ b/client/src/app/metamodel/store/actions/attribute.actions.ts
@@ -5,3 +5,12 @@ import { Attribute } from '../models';
 export const loadAttributeList = createAction('[Metamodel] Load Attribute List');
 export const loadAttributeListSuccess = createAction('[Metamodel] Load Attribute List Success', props<{ attributes: Attribute[] }>());
 export const loadAttributeListFail = createAction('[Metamodel] Load Attribute List Fail');
+export const addAttribute = createAction('[Metamodel] Add Attribute', props<{ attribute: Attribute }>());
+export const addAttributeSuccess = createAction('[Metamodel] Add Attribute Success', props<{ attribute: Attribute }>());
+export const addAttributeFail = createAction('[Metamodel] Add Attribute Fail');
+export const editAttribute = createAction('[Metamodel] Edit Attribute', props<{ attribute: Attribute }>());
+export const editAttributeSuccess = createAction('[Metamodel] Edit Attribute Success', props<{ attribute: Attribute }>());
+export const editAttributeFail = createAction('[Metamodel] Edit Attribute Fail');
+export const deleteAttribute = createAction('[Metamodel] Delete Attribute', props<{ attribute: Attribute }>());
+export const deleteAttributeSuccess = createAction('[Metamodel] Delete Attribute Success', props<{ attribute: Attribute }>());
+export const deleteAttributeFail = createAction('[Metamodel] Delete Attribute Fail');
diff --git a/client/src/app/metamodel/store/actions/column.actions.ts b/client/src/app/metamodel/store/actions/column.actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1aa0d0ec7e33827040631c7c18b3d388e51dc330
--- /dev/null
+++ b/client/src/app/metamodel/store/actions/column.actions.ts
@@ -0,0 +1,7 @@
+import { createAction, props } from '@ngrx/store';
+
+import { Column } from '../models';
+
+export const loadColumnList = createAction('[Metamodel] Load Column List');
+export const loadColumnListSuccess = createAction('[Metamodel] Load Column List Success', props<{ columns: Column[] }>());
+export const loadColumnListFail = createAction('[Metamodel] Load Column List Fail');
diff --git a/client/src/app/metamodel/store/effects/attribute.effects.ts b/client/src/app/metamodel/store/effects/attribute.effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dfb8c6892b703b9d5e69a8d0fc0b9583073e458b
--- /dev/null
+++ b/client/src/app/metamodel/store/effects/attribute.effects.ts
@@ -0,0 +1,123 @@
+import { Injectable } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects';
+import { Store } from '@ngrx/store';
+import { of } from 'rxjs';
+import { map, tap, mergeMap, catchError } from 'rxjs/operators';
+import { ToastrService } from 'ngx-toastr';
+
+import * as attributeActions from '../actions/attribute.actions';
+import { AttributeService } from '../services/attribute.service';
+import * as datasetSelector from '../selectors/dataset.selector';
+ 
+@Injectable()
+export class AttributeEffects {
+    loadAttributes$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(attributeActions.loadAttributeList),
+            concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
+            mergeMap(([action, datasetName]) => this.attributeService.retrieveAttributeList(datasetName)
+                .pipe(
+                    map(attributes => attributeActions.loadAttributeListSuccess({ attributes })),
+                    catchError(() => of(attributeActions.loadAttributeListFail()))
+                )
+            )
+        )
+    );
+    
+    addAttribute$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(attributeActions.addAttribute),
+            concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
+            mergeMap(([action, datasetName]) => this.attributeService.addAttribute(datasetName, action.attribute)
+                .pipe(
+                    map(attribute => attributeActions.addAttributeSuccess({ attribute })),
+                    catchError(() => of(attributeActions.addAttributeFail()))
+                )
+            )
+        )
+    );
+
+    addAttributeSuccess$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(attributeActions.addAttributeSuccess),
+            tap(() => {
+                this.router.navigate(['/admin/attribute/attribute-list']);
+                this.toastr.success('Attribute successfully added', 'The new attribute was added into the database')
+            })
+        ), { dispatch: false}
+    );
+
+    addAttributeFail$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(attributeActions.addAttributeFail),
+            tap(() => this.toastr.error('Failure to add attribute', 'The new attribute could not be added into the database'))
+        ), { dispatch: false}
+    );
+
+    editAttribute$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(attributeActions.editAttribute),
+            concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
+            mergeMap(([action, datasetName]) => this.attributeService.editAttribute(datasetName, action.attribute)
+                .pipe(
+                    map(attribute => attributeActions.editAttributeSuccess({ attribute })),
+                    catchError(() => of(attributeActions.editAttributeFail()))
+                )
+            )
+        )
+    );
+
+    editAttributeSuccess$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(attributeActions.editAttributeSuccess),
+            tap(() => {
+                this.router.navigate(['/admin/attribute/attribute-list']);
+                this.toastr.success('Attribute successfully edited', 'The existing attribute has been edited into the database')
+            })
+        ), { dispatch: false}
+    );
+
+    editAttributeFail$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(attributeActions.editAttributeFail),
+            tap(() => this.toastr.error('Failure to edit attribute', 'The existing attribute could not be edited into the database'))
+        ), { dispatch: false}
+    );
+
+    deleteAttribute$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(attributeActions.deleteAttribute),
+            concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
+            mergeMap(([action, datasetName]) => this.attributeService.deleteAttribute(datasetName, action.attribute)
+                .pipe(
+                    map(() => attributeActions.deleteAttributeSuccess({ attribute: action.attribute })),
+                    catchError(() => of(attributeActions.deleteAttributeFail()))
+                )
+            )
+        )
+    );
+
+    deleteAttributeSuccess$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(attributeActions.deleteAttributeSuccess),
+            tap(() => this.toastr.success('Attribute successfully deleted', 'The existing attribute has been deleted'))
+        ), { dispatch: false}
+    );
+
+    deleteAttributeFail$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(attributeActions.deleteAttributeFail),
+            tap(() => this.toastr.error('Failure to delete attribute', 'The existing attribute could not be deleted from the database'))
+        ), { dispatch: false}
+    );
+ 
+    constructor(
+        private actions$: Actions,
+        private attributeService: AttributeService,
+        private router: Router,
+        private toastr: ToastrService,
+        private store: Store<{ }>
+    ) {}
+}
diff --git a/client/src/app/metamodel/store/effects/column.effects.ts b/client/src/app/metamodel/store/effects/column.effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cc6c7304d452378b4b09e66d73d655913ebf22ff
--- /dev/null
+++ b/client/src/app/metamodel/store/effects/column.effects.ts
@@ -0,0 +1,32 @@
+import { Injectable } from '@angular/core';
+
+import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects';
+import { Store } from '@ngrx/store';
+import { of } from 'rxjs';
+import { map, mergeMap, catchError } from 'rxjs/operators';
+
+import * as columnActions from '../actions/column.actions';
+import { ColumnService } from '../services/column.service';
+import * as datasetSelector from '../selectors/dataset.selector';
+ 
+@Injectable()
+export class ColumnEffects {
+    loadColumns$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(columnActions.loadColumnList),
+            concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
+            mergeMap(([action, datasetName]) => this.columnService.retrieveColumns(datasetName)
+                .pipe(
+                    map(columns => columnActions.loadColumnListSuccess({ columns })),
+                    catchError(() => of(columnActions.loadColumnListFail()))
+                )
+            )
+        )
+    );
+ 
+    constructor(
+        private actions$: Actions,
+        private columnService: ColumnService,
+        private store: Store<{ }>
+    ) {}
+}
diff --git a/client/src/app/metamodel/store/effects/index.ts b/client/src/app/metamodel/store/effects/index.ts
index 5b5a627d1c463a8e84ae16d2b4e617e59f0ab9d0..7fa91a8f0f1fdead70a6904845baf15aee06df30 100644
--- a/client/src/app/metamodel/store/effects/index.ts
+++ b/client/src/app/metamodel/store/effects/index.ts
@@ -1,9 +1,11 @@
 import { DatabaseEffects } from './database.effects';
 import { TableEffects } from './table.effects';
+import { ColumnEffects } from './column.effects';
 import { SurveyEffects } from './survey.effects';
 import { InstanceEffects } from './instance.effects';
 import { DatasetFamilyEffects } from './dataset-family.effects';
 import { DatasetEffects } from './dataset.effects';
+import { AttributeEffects } from './attribute.effects';
 import { GroupEffects } from './group.effects';
 import { CriteriaFamilyEffects } from './criteria-family.effects';
 import { OutputCategoryEffects } from './output-category.effects';
@@ -15,10 +17,12 @@ import { SelectOptionEffects } from './select-option.effects';
 export const metamodelEffects = [
     DatabaseEffects,
     TableEffects,
+    ColumnEffects,
     SurveyEffects,
     InstanceEffects,
     DatasetFamilyEffects,
     DatasetEffects,
+    AttributeEffects,
     GroupEffects,
     CriteriaFamilyEffects,
     OutputCategoryEffects,
diff --git a/client/src/app/metamodel/store/effects/output-family.effects.ts b/client/src/app/metamodel/store/effects/output-family.effects.ts
index e5b5d0622b3ab4c57205d6f892b7f3b075ad1e5c..86140a8fb48bd2385aee3ea0ec0d222aba544c78 100644
--- a/client/src/app/metamodel/store/effects/output-family.effects.ts
+++ b/client/src/app/metamodel/store/effects/output-family.effects.ts
@@ -12,7 +12,7 @@ import * as datasetSelector from '../selectors/dataset.selector';
  
 @Injectable()
 export class OutputFamilyEffects {
-    loadOutputFamilys$ = createEffect(() =>
+    loadOutputFamilies$ = createEffect(() =>
         this.actions$.pipe(
             ofType(outputFamilyActions.loadOutputFamilyList),
             concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
@@ -25,7 +25,7 @@ export class OutputFamilyEffects {
         )
     );
 
-    addOutputFamily$ = createEffect(() => 
+    addOutputFamilies$ = createEffect(() => 
         this.actions$.pipe(
             ofType(outputFamilyActions.addOutputFamily),
             concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
diff --git a/client/src/app/metamodel/store/models/db-table-column.model.ts b/client/src/app/metamodel/store/models/column.model.ts
similarity index 54%
rename from client/src/app/metamodel/store/models/db-table-column.model.ts
rename to client/src/app/metamodel/store/models/column.model.ts
index 10fc6f8931e5b1a60d778de137364e700f968219..91d30c9af4415413e3714858be2bc0680b9f7c97 100644
--- a/client/src/app/metamodel/store/models/db-table-column.model.ts
+++ b/client/src/app/metamodel/store/models/column.model.ts
@@ -1,4 +1,4 @@
-export interface DbTableColumn {
+export interface Column {
     name: string;
     type: string;
 };
diff --git a/client/src/app/metamodel/store/models/index.ts b/client/src/app/metamodel/store/models/index.ts
index d55be226c231db4f978fba312334540ad2f15847..c2af97131216e59b1aadddd0ee5a2be18764d61f 100644
--- a/client/src/app/metamodel/store/models/index.ts
+++ b/client/src/app/metamodel/store/models/index.ts
@@ -1,5 +1,4 @@
 export * from './database.model';
-export * from './db-table-column.model';
 export * from './survey.model';
 export * from './group.model';
 export * from './dataset.model';
@@ -12,3 +11,4 @@ export * from './output-family.model';
 export * from './select.model';
 export * from './select-option.model';
 export * from './file-info.model';
+export * from './column.model';
diff --git a/client/src/app/metamodel/store/reducers/attribute.reducer.ts b/client/src/app/metamodel/store/reducers/attribute.reducer.ts
index e57fde5a349f37895559259df59eef56b5afb571..964a1829dfce8060fa8e4e09e08df36a9383c578 100644
--- a/client/src/app/metamodel/store/reducers/attribute.reducer.ts
+++ b/client/src/app/metamodel/store/reducers/attribute.reducer.ts
@@ -39,6 +39,15 @@ export const attributeReducer = createReducer(
             ...state,
             attributeListIsLoading: false
         }
+    }),
+    on(attributeActions.addAttributeSuccess, (state, { attribute }) => {
+        return adapter.addOne(attribute, state)
+    }),
+    on(attributeActions.editAttributeSuccess, (state, { attribute }) => {
+        return adapter.setOne(attribute, state)
+    }),
+    on(attributeActions.deleteAttributeSuccess, (state, { attribute }) => {
+        return adapter.removeOne(attribute.name, state)
     })
 );
 
@@ -53,3 +62,6 @@ export const selectAttributeIds = selectIds;
 export const selectAttributeEntities = selectEntities;
 export const selectAllAttributes = selectAll;
 export const selectAttributeTotal = selectTotal;
+
+export const selectAttributeListIsLoading = (state: State) => state.attributeListIsLoading;
+export const selectAttributeListIsLoaded = (state: State) => state.attributeListIsLoaded;
diff --git a/client/src/app/metamodel/store/reducers/column.reducer.ts b/client/src/app/metamodel/store/reducers/column.reducer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2f8aa770a132239a27edfce7379a83d8468a647f
--- /dev/null
+++ b/client/src/app/metamodel/store/reducers/column.reducer.ts
@@ -0,0 +1,61 @@
+import { createReducer, on } from '@ngrx/store';
+import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
+
+import { Column } from '../models';
+import * as columnActions from '../actions/column.actions';
+
+export interface State extends EntityState<Column> {
+    columnListIsLoading: boolean;
+    columnListIsLoaded: boolean;
+}
+
+export const adapter: EntityAdapter<Column> = createEntityAdapter<Column>({
+    selectId: (column: Column) => column.name,
+    sortComparer: (a: Column, b: Column) => a.name.localeCompare(b.name)
+});
+
+export const initialState: State = adapter.getInitialState({
+    columnListIsLoading: false,
+    columnListIsLoaded: false
+});
+
+export const columnReducer = createReducer(
+    initialState,
+    on(columnActions.loadColumnList, (state) => {
+        return {
+            ...state,
+            columnListIsLoading: true
+        }
+    }),
+    on(columnActions.loadColumnListSuccess, (state, { columns }) => {
+        return adapter.setAll(
+            columns, 
+            { 
+                ...state,
+                columnListIsLoading: false,
+                columnListIsLoaded: true
+            }
+        );
+    }),
+    on(columnActions.loadColumnListFail, (state) => {
+        return {
+            ...state,
+            columnListIsLoading: false
+        }
+    })
+);
+
+const {
+    selectIds,
+    selectEntities,
+    selectAll,
+    selectTotal,
+} = adapter.getSelectors();
+
+export const selectColumnIds = selectIds;
+export const selectColumnEntities = selectEntities;
+export const selectAllColumns = selectAll;
+export const selectColumnTotal = selectTotal;
+
+export const selectColumnListIsLoading = (state: State) => state.columnListIsLoading;
+export const selectColumnListIsLoaded = (state: State) => state.columnListIsLoaded;
diff --git a/client/src/app/metamodel/store/reducers/index.ts b/client/src/app/metamodel/store/reducers/index.ts
index a37e1a627deffbd7ad0e36177f507973cea5458a..f187b7bd3046ad7cfb85c8921843647d5f160824 100644
--- a/client/src/app/metamodel/store/reducers/index.ts
+++ b/client/src/app/metamodel/store/reducers/index.ts
@@ -3,6 +3,7 @@ import { combineReducers, createFeatureSelector } from '@ngrx/store';
 import { RouterReducerState } from 'src/app/custom-route-serializer';
 import * as database from './database.reducer';
 import * as table from './table.reducer';
+import * as column from './column.reducer';
 import * as survey from './survey.reducer';
 import * as group from './group.reducer';
 import * as dataset from './dataset.reducer';
@@ -19,6 +20,7 @@ import * as selectOption from './select-option.reducer';
 export interface State {
     database: database.State;
     table: table.State;
+    column: column.State;
     survey: survey.State;
     group: group.State;
     dataset: dataset.State;
@@ -36,6 +38,7 @@ export interface State {
 const reducers = {
     database: database.databaseReducer,
     table: table.tableReducer,
+    column: column.columnReducer,
     survey: survey.surveyReducer,
     group: group.groupReducer,
     dataset: dataset.datasetReducer,
diff --git a/client/src/app/metamodel/store/selectors/attribute.selector.ts b/client/src/app/metamodel/store/selectors/attribute.selector.ts
index 8dcfa5940ebf2461b316b580c9ede8fe98fde756..d53216d586122a2d842a8d645412563327956116 100644
--- a/client/src/app/metamodel/store/selectors/attribute.selector.ts
+++ b/client/src/app/metamodel/store/selectors/attribute.selector.ts
@@ -27,3 +27,13 @@ export const selectAttributeTotal = createSelector(
     selectAttributeState,
     fromAttribute.selectAttributeTotal
 );
+
+export const selectAttributeListIsLoading = createSelector(
+    selectAttributeState,
+    fromAttribute.selectAttributeListIsLoading
+);
+
+export const selectAttributeListIsLoaded = createSelector(
+    selectAttributeState,
+    fromAttribute.selectAttributeListIsLoaded
+);
diff --git a/client/src/app/metamodel/store/selectors/column.selector.ts b/client/src/app/metamodel/store/selectors/column.selector.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1f3da9e9d671f934399b0a2f9daeebf57064003c
--- /dev/null
+++ b/client/src/app/metamodel/store/selectors/column.selector.ts
@@ -0,0 +1,45 @@
+import { createSelector } from '@ngrx/store';
+
+import * as reducer from '../reducers';
+import * as fromColumn from '../reducers/column.reducer';
+
+export const selectColumnState = createSelector(
+    reducer.getMetamodelState,
+    (state: reducer.State) => state.column
+);
+
+export const selectColumnIds = createSelector(
+    selectColumnState,
+    fromColumn.selectColumnIds
+);
+
+export const selectColumnEntities = createSelector(
+    selectColumnState,
+    fromColumn.selectColumnEntities
+);
+
+export const selectAllColumns = createSelector(
+    selectColumnState,
+    fromColumn.selectAllColumns
+);
+
+export const selectColumnTotal = createSelector(
+    selectColumnState,
+    fromColumn.selectColumnTotal
+);
+
+export const selectColumnListIsLoading = createSelector(
+    selectColumnState,
+    fromColumn.selectColumnListIsLoading
+);
+
+export const selectColumnListIsLoaded = createSelector(
+    selectColumnState,
+    fromColumn.selectColumnListIsLoaded
+);
+
+export const selectColumnByRouteName = createSelector(
+    selectColumnEntities,
+    reducer.selectRouterState,
+    (entities, router) => entities[router.state.params.name]
+);
diff --git a/client/src/app/metamodel/store/services/column.service.ts b/client/src/app/metamodel/store/services/column.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7b637838c9cd6b934a64b41299a4e0f2a33347b1
--- /dev/null
+++ b/client/src/app/metamodel/store/services/column.service.ts
@@ -0,0 +1,18 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+import { Observable } from 'rxjs';
+
+import { Column } from '../models';
+import { environment } from 'src/environments/environment';
+
+@Injectable()
+export class ColumnService {
+    private API_PATH: string = environment.apiUrl + '/';
+
+    constructor(private http: HttpClient) { }
+
+    retrieveColumns(datasetName: string): Observable<Column[]> {
+        return this.http.get<Column[]>(this.API_PATH + 'dataset/' + datasetName + '/column');
+    }
+}
diff --git a/client/src/app/metamodel/store/services/database.service.ts b/client/src/app/metamodel/store/services/database.service.ts
index 74f5ca325e9a2cd2d0f2940743209176d571afec..828c7c6f305f5ad9f4963ff3b16505c6af0a6417 100644
--- a/client/src/app/metamodel/store/services/database.service.ts
+++ b/client/src/app/metamodel/store/services/database.service.ts
@@ -3,7 +3,7 @@ import { HttpClient } from '@angular/common/http';
 
 import { Observable } from 'rxjs';
 
-import { Database, DbTableColumn } from '../models';
+import { Database } from '../models';
 import { environment } from 'src/environments/environment';
 
 @Injectable()
@@ -15,10 +15,6 @@ export class DatabaseService {
     retrieveDatabaseList(): Observable<Database[]> {
         return this.http.get<Database[]>(this.API_PATH + 'database');
     }
-    
-    retrieveColumns(datasetName: string) {
-        return this.http.get<DbTableColumn[]>(this.API_PATH + 'dataset/' + datasetName + '/column');
-    }
 
     addDatabase(newDatabase: Database): Observable<Database> {
         return this.http.post<Database>(this.API_PATH + 'database', newDatabase);
diff --git a/client/src/app/metamodel/store/services/index.ts b/client/src/app/metamodel/store/services/index.ts
index fe9f8660b644928767f0bca7549305a1d3d488b3..c04a44fd0f0d07748bb07730d9940cc9579ecb40 100644
--- a/client/src/app/metamodel/store/services/index.ts
+++ b/client/src/app/metamodel/store/services/index.ts
@@ -1,5 +1,6 @@
 import { DatabaseService } from './database.service';
 import { TableService } from './table.service';
+import { ColumnService } from './column.service';
 import { SurveyService } from './survey.service';
 import { GroupService } from './group.service';
 import { DatasetService } from './dataset.service';
@@ -16,6 +17,7 @@ import { SelectOptionService } from './select-option.service';
 export const metamodelServices = [
     DatabaseService,
     TableService,
+    ColumnService,
     SurveyService,
     GroupService,
     DatasetService,