diff --git a/frontend/app_flowy/lib/plugins/board/board.dart b/frontend/app_flowy/lib/plugins/board/board.dart
index c55d7f2e17..213cc8bc3c 100644
--- a/frontend/app_flowy/lib/plugins/board/board.dart
+++ b/frontend/app_flowy/lib/plugins/board/board.dart
@@ -31,7 +31,7 @@ class BoardPluginBuilder implements PluginBuilder {
 
 class BoardPluginConfig implements PluginConfig {
   @override
-  bool get creatable => false;
+  bool get creatable => true;
 }
 
 class BoardPlugin extends Plugin {
diff --git a/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart b/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart
index 8c46ce18b2..c8b6873d91 100644
--- a/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart
+++ b/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart
@@ -3,7 +3,6 @@ import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/board_card.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
index 0559cfa364..dabb77e1de 100644
--- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
+++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
@@ -168,10 +168,12 @@ impl GridRevisionEditor {
 
     pub async fn update_field(&self, params: FieldChangesetParams) -> FlowyResult<()> {
         let field_id = params.field_id.clone();
-        let field_type: Option<FieldType> = match self.grid_pad.read().await.get_field_rev(params.field_id.as_str()) {
-            None => None,
-            Some((_, field_rev)) => Some(field_rev.ty.into()),
-        };
+        let field_type: Option<FieldType> = self
+            .grid_pad
+            .read()
+            .await
+            .get_field_rev(params.field_id.as_str())
+            .map(|(_, field_rev)| field_rev.ty.into());
 
         match field_type {
             None => Err(ErrorCode::FieldDoesNotExist.into()),
@@ -341,7 +343,7 @@ impl GridRevisionEditor {
         Ok(row_pb)
     }
 
-    pub async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<GroupsChangesetPB> {
+    pub async fn move_group(&self, _params: MoveGroupParams) -> FlowyResult<GroupsChangesetPB> {
         //
         todo!()
     }
diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs
index 9ab6f4801a..a9ade3f194 100644
--- a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs
+++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs
@@ -183,7 +183,7 @@ impl GridViewRevisionEditor {
         grid_setting
     }
 
-    pub(crate) async fn update_setting(&self, changeset: GridSettingChangesetParams) -> FlowyResult<()> {
+    pub(crate) async fn update_setting(&self, _changeset: GridSettingChangesetParams) -> FlowyResult<()> {
         // let _ = self.modify(|pad| Ok(pad.update_setting(changeset)?)).await;
         // Ok(())
         todo!()
@@ -207,6 +207,7 @@ impl GridViewRevisionEditor {
             .send();
     }
 
+    #[allow(dead_code)]
     async fn modify<F>(&self, f: F) -> FlowyResult<()>
     where
         F: for<'a> FnOnce(&'a mut GridViewRevisionPad) -> FlowyResult<Option<GridViewRevisionChangeset>>,
@@ -230,7 +231,7 @@ async fn apply_change(
     let GridViewRevisionChangeset { delta, md5 } = change;
     let (base_rev_id, rev_id) = rev_manager.next_rev_id_pair();
     let delta_data = delta.json_bytes();
-    let revision = Revision::new(&rev_manager.object_id, base_rev_id, rev_id, delta_data, &user_id, md5);
+    let revision = Revision::new(&rev_manager.object_id, base_rev_id, rev_id, delta_data, user_id, md5);
     let _ = rev_manager.add_local_revision(&revision).await?;
     Ok(())
 }
@@ -287,49 +288,29 @@ impl GroupConfigurationWriter for GroupConfigurationWriterImpl {
         &self,
         field_id: &str,
         field_type: FieldTypeRevision,
-        group_id: &str,
-        mut_fn: impl FnOnce(&mut GroupConfigurationRevision),
+        configuration_id: &str,
+        content: String,
     ) -> AFFuture<FlowyResult<()>> {
         let user_id = self.user_id.clone();
-        let group_id = group_id.to_owned();
+        let configuration_id = configuration_id.to_owned();
         let rev_manager = self.rev_manager.clone();
         let view_pad = self.view_pad.clone();
         let field_id = field_id.to_owned();
 
         wrap_future(async move {
-            match view_pad
-                .write()
-                .await
-                .get_mut_group(&field_id, &field_type, &group_id, mut_fn)?
-            {
+            match view_pad.write().await.get_mut_group(
+                &field_id,
+                &field_type,
+                &configuration_id,
+                |group_configuration| {
+                    group_configuration.content = content;
+                },
+            )? {
                 None => Ok(()),
                 Some(changeset) => apply_change(&user_id, rev_manager, changeset).await,
             }
         })
     }
-    // fn save_group_configuration(
-    //     &self,
-    //     field_id: &str,
-    //     field_type: FieldTypeRevision,
-    //
-    //     configuration: GroupConfigurationRevision,
-    // ) -> AFFuture<FlowyResult<()>> {
-    //     let user_id = self.user_id.clone();
-    //     let rev_manager = self.rev_manager.clone();
-    //     let view_pad = self.view_pad.clone();
-    //     let field_id = field_id.to_owned();
-    //
-    //     wrap_future(async move {
-    //         match view_pad
-    //             .write()
-    //             .await
-    //             .insert_group_configuration(&field_id, &field_type, configuration)?
-    //         {
-    //             None => Ok(()),
-    //             Some(changeset) => apply_change(&user_id, rev_manager, changeset).await,
-    //         }
-    //     })
-    // }
 }
 
 pub fn make_grid_setting(view_pad: &GridViewRevisionPad, field_revs: &[Arc<FieldRevision>]) -> GridSettingPB {
diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs
index 42c4ff1c65..5386bdb893 100644
--- a/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs
+++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs
@@ -131,6 +131,7 @@ impl GridViewManager {
         }
     }
 
+    #[allow(dead_code)]
     pub(crate) async fn move_group(&self) {}
 
     pub(crate) async fn get_view_editor(&self, view_id: &str) -> FlowyResult<Arc<GridViewRevisionEditor>> {
diff --git a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs
index 4fe59fbaca..15cbe6c61f 100644
--- a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs
+++ b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs
@@ -1,8 +1,9 @@
 use crate::services::group::Group;
 use flowy_error::FlowyResult;
 use flowy_grid_data_model::revision::{
-    FieldRevision, FieldTypeRevision, GroupConfigurationContentSerde, GroupConfigurationRevision, GroupRecordRevision,
+    FieldRevision, FieldTypeRevision, GroupConfigurationContent, GroupConfigurationRevision, GroupRecordRevision,
 };
+
 use lib_infra::future::AFFuture;
 use std::sync::Arc;
 
@@ -10,34 +11,28 @@ pub trait GroupConfigurationReader: Send + Sync + 'static {
     fn get_group_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<Arc<GroupConfigurationRevision>>;
 }
 
-pub trait GroupConfigurationWriter: Send + Sync + 'static + ?Sized {
+pub trait GroupConfigurationWriter: Send + Sync + 'static {
     fn save_group_configuration(
         &self,
         field_id: &str,
         field_type: FieldTypeRevision,
-        group_id: &str,
-        mut_fn: fn(&mut GroupConfigurationRevision),
+        configuration_id: &str,
+        content: String,
     ) -> AFFuture<FlowyResult<()>>;
 }
 
-pub trait GroupConfigurationAction: Send + Sync {
-    fn group_records(&self) -> &[GroupRecordRevision];
-    fn merge_groups(&self, groups: Vec<Group>) -> FlowyResult<()>;
-    fn hide_group(&self, group_id: &str) -> FlowyResult<()>;
-    fn show_group(&self, group_id: &str) -> FlowyResult<()>;
-}
-
 pub struct GenericGroupConfiguration<C> {
+    pub configuration: C,
+    // pub groups_map: IndexMap<String, Group>,
+    configuration_id: String,
     field_rev: Arc<FieldRevision>,
     reader: Arc<dyn GroupConfigurationReader>,
-    configuration_rev: Arc<GroupConfigurationRevision>,
     writer: Arc<dyn GroupConfigurationWriter>,
-    pub(crate) configuration: C,
 }
 
 impl<C> GenericGroupConfiguration<C>
 where
-    C: GroupConfigurationContentSerde,
+    C: GroupConfigurationContent,
 {
     pub async fn new(
         field_rev: Arc<FieldRevision>,
@@ -45,17 +40,58 @@ where
         writer: Arc<dyn GroupConfigurationWriter>,
     ) -> FlowyResult<Self> {
         let configuration_rev = reader.get_group_configuration(field_rev.clone()).await;
+        let configuration_id = configuration_rev.id.clone();
         let configuration = C::from_configuration_content(&configuration_rev.content)?;
         Ok(Self {
+            configuration_id,
             field_rev,
-            configuration_rev,
             reader,
             writer,
             configuration,
         })
     }
 
-    pub async fn save_configuration(&self) {}
+    #[allow(dead_code)]
+    fn group_records(&self) -> &[GroupRecordRevision] {
+        todo!()
+    }
+    pub(crate) async fn merge_groups(&mut self, groups: &[Group]) -> FlowyResult<()> {
+        match merge_groups(self.configuration.get_groups(), groups) {
+            None => Ok(()),
+            Some(new_groups) => {
+                self.configuration.set_groups(new_groups);
+                let _ = self.save_configuration().await?;
+                Ok(())
+            }
+        }
+    }
+
+    #[allow(dead_code)]
+    pub(crate) async fn hide_group(&mut self, group_id: &str) -> FlowyResult<()> {
+        self.configuration.mut_group(group_id, |group_rev| {
+            group_rev.visible = false;
+        });
+        let _ = self.save_configuration().await?;
+        Ok(())
+    }
+
+    #[allow(dead_code)]
+    pub(crate) async fn show_group(&mut self, group_id: &str) -> FlowyResult<()> {
+        self.configuration.mut_group(group_id, |group_rev| {
+            group_rev.visible = true;
+        });
+        let _ = self.save_configuration().await?;
+        Ok(())
+    }
+
+    pub async fn save_configuration(&self) -> FlowyResult<()> {
+        let content = self.configuration.to_configuration_content()?;
+        let _ = self
+            .writer
+            .save_group_configuration(&self.field_rev.id, self.field_rev.ty, &self.configuration_id, content)
+            .await?;
+        Ok(())
+    }
 }
 
 impl<T> GroupConfigurationReader for Arc<T>
@@ -67,25 +103,29 @@ where
     }
 }
 
-impl<T> GroupConfigurationWriter for Arc<T>
-where
-    T: GroupConfigurationWriter,
-{
-    fn save_group_configuration(
-        &self,
-        field_id: &str,
-        field_type: FieldTypeRevision,
-        group_id: &str,
-        mut_fn: impl FnOnce(&mut GroupConfigurationRevision),
-    ) -> AFFuture<FlowyResult<()>> {
-        todo!()
+fn merge_groups(old_group: &[GroupRecordRevision], groups: &[Group]) -> Option<Vec<GroupRecordRevision>> {
+    // tracing::trace!("Merge group: old: {}, new: {}", old_group.len(), groups.len());
+    if old_group.is_empty() {
+        let new_groups = groups
+            .iter()
+            .map(|group| GroupRecordRevision::new(group.id.clone()))
+            .collect();
+        return Some(new_groups);
     }
-    // fn save_group_configuration(
-    //     &self,
-    //     field_id: &str,
-    //     field_type: FieldTypeRevision,
-    //     configuration: GroupConfigurationRevision,
-    // ) -> AFFuture<FlowyResult<()>> {
-    //     (**self).save_group_configuration(field_id, field_type, configuration)
-    // }
+
+    let new_groups = groups
+        .iter()
+        .filter(|group| !old_group.iter().any(|group_rev| group_rev.group_id == group.id))
+        .collect::<Vec<&Group>>();
+
+    if new_groups.is_empty() {
+        return None;
+    }
+
+    let mut old_group = old_group.to_vec();
+    let new_groups = new_groups
+        .iter()
+        .map(|group| GroupRecordRevision::new(group.id.clone()));
+    old_group.extend(new_groups);
+    Some(old_group)
 }
diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs
similarity index 88%
rename from frontend/rust-lib/flowy-grid/src/services/group/group_controller.rs
rename to frontend/rust-lib/flowy-grid/src/services/group/controller.rs
index 97755fd0ee..44344fa0a1 100644
--- a/frontend/rust-lib/flowy-grid/src/services/group/group_controller.rs
+++ b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs
@@ -1,15 +1,14 @@
 use crate::entities::{GroupRowsChangesetPB, RowPB};
 use crate::services::cell::{decode_any_cell_data, CellBytesParser};
 use crate::services::group::action::GroupAction;
-use crate::services::group::configuration::{GenericGroupConfiguration, GroupConfigurationReader};
+use crate::services::group::configuration::GenericGroupConfiguration;
 use crate::services::group::entities::Group;
 use flowy_error::FlowyResult;
 use flowy_grid_data_model::revision::{
-    FieldRevision, GroupConfigurationContentSerde, RowChangeset, RowRevision, TypeOptionDataDeserializer,
+    FieldRevision, GroupConfigurationContent, RowChangeset, RowRevision, TypeOptionDataDeserializer,
 };
 use indexmap::IndexMap;
 
-use crate::services::group::GroupConfigurationWriter;
 use std::marker::PhantomData;
 use std::sync::Arc;
 
@@ -18,7 +17,7 @@ const DEFAULT_GROUP_ID: &str = "default_group";
 // Each kind of group must implement this trait to provide custom group
 // operations. For example, insert cell data to the row_rev when creating
 // a new row.
-pub trait GroupController: GroupControllerSharedAction + Send + Sync {
+pub trait GroupController: GroupControllerSharedOperation + Send + Sync {
     fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str);
 }
 
@@ -34,9 +33,10 @@ pub trait GroupGenerator {
 }
 
 // Defines the shared actions each group controller can perform.
-pub trait GroupControllerSharedAction: Send + Sync {
+pub trait GroupControllerSharedOperation: Send + Sync {
     // The field that is used for grouping the rows
     fn field_id(&self) -> &str;
+    fn groups(&self) -> &[Group];
     fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<Vec<Group>>;
     fn did_update_row(
         &mut self,
@@ -66,32 +66,28 @@ pub trait GroupControllerSharedAction: Send + Sync {
 pub struct GenericGroupController<C, T, G, P> {
     pub field_id: String,
     pub groups_map: IndexMap<String, Group>,
-
-    /// default_group is used to store the rows that don't belong to any groups.
-    default_group: Group,
     pub type_option: Option<T>,
     pub configuration: GenericGroupConfiguration<C>,
+    /// default_group is used to store the rows that don't belong to any groups.
+    default_group: Group,
     group_action_phantom: PhantomData<G>,
     cell_parser_phantom: PhantomData<P>,
 }
 
 impl<C, T, G, P> GenericGroupController<C, T, G, P>
 where
-    C: GroupConfigurationContentSerde,
+    C: GroupConfigurationContent,
     T: TypeOptionDataDeserializer,
     G: GroupGenerator<ConfigurationType = GenericGroupConfiguration<C>, TypeOptionType = T>,
 {
     pub async fn new(
         field_rev: &Arc<FieldRevision>,
-        configuration_reader: Arc<dyn GroupConfigurationReader>,
-        configuration_writer: Arc<dyn GroupConfigurationWriter>,
+        mut configuration: GenericGroupConfiguration<C>,
     ) -> FlowyResult<Self> {
-        let configuration =
-            GenericGroupConfiguration::<C>::new(field_rev.clone(), configuration_reader, configuration_writer).await?;
         let field_type_rev = field_rev.ty;
         let type_option = field_rev.get_type_option_entry::<T>(field_type_rev);
         let groups = G::generate_groups(&field_rev.id, &configuration, &type_option);
-
+        let _ = configuration.merge_groups(&groups).await?;
         let default_group = Group::new(
             DEFAULT_GROUP_ID.to_owned(),
             field_rev.id.clone(),
@@ -111,20 +107,21 @@ where
     }
 }
 
-impl<C, T, G, P> GroupControllerSharedAction for GenericGroupController<C, T, G, P>
+impl<C, T, G, P> GroupControllerSharedOperation for GenericGroupController<C, T, G, P>
 where
     P: CellBytesParser,
+    C: GroupConfigurationContent,
     Self: GroupAction<CellDataType = P::Object>,
 {
     fn field_id(&self) -> &str {
         &self.field_id
     }
 
-    fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<Vec<Group>> {
-        // if self.configuration.is_none() {
-        //     return Ok(vec![]);
-        // }
+    fn groups(&self) -> &[Group] {
+        todo!()
+    }
 
+    fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<Vec<Group>> {
         for row_rev in row_revs {
             if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
                 let mut group_rows: Vec<GroupRow> = vec![];
diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs
index dc278af7f5..59a53ae72a 100644
--- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs
+++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs
@@ -1,13 +1,11 @@
 use crate::entities::GroupRowsChangesetPB;
 use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
 use crate::services::group::action::GroupAction;
-use crate::services::group::configuration::{GenericGroupConfiguration, GroupConfigurationAction};
+use crate::services::group::configuration::GenericGroupConfiguration;
+use crate::services::group::controller::{GenericGroupController, GroupController, GroupGenerator};
 use crate::services::group::entities::Group;
-use crate::services::group::group_controller::{GenericGroupController, GroupController, GroupGenerator};
-use flowy_error::FlowyResult;
-use flowy_grid_data_model::revision::{
-    CheckboxGroupConfigurationRevision, FieldRevision, GroupRecordRevision, RowChangeset, RowRevision,
-};
+
+use flowy_grid_data_model::revision::{CheckboxGroupConfigurationRevision, FieldRevision, RowChangeset, RowRevision};
 
 pub type CheckboxGroupController = GenericGroupController<
     CheckboxGroupConfigurationRevision,
@@ -17,23 +15,6 @@ pub type CheckboxGroupController = GenericGroupController<
 >;
 
 pub type CheckboxGroupConfiguration = GenericGroupConfiguration<CheckboxGroupConfigurationRevision>;
-impl GroupConfigurationAction for CheckboxGroupConfiguration {
-    fn group_records(&self) -> &[GroupRecordRevision] {
-        &[]
-    }
-
-    fn merge_groups(&self, groups: Vec<Group>) -> FlowyResult<()> {
-        Ok(())
-    }
-
-    fn hide_group(&self, group_id: &str) -> FlowyResult<()> {
-        Ok(())
-    }
-
-    fn show_group(&self, group_id: &str) -> FlowyResult<()> {
-        Ok(())
-    }
-}
 
 impl GroupAction for CheckboxGroupController {
     type CellDataType = CheckboxCellData;
@@ -82,8 +63,8 @@ impl GroupGenerator for CheckboxGroupGenerator {
 
     fn generate_groups(
         field_id: &str,
-        configuration: &Self::ConfigurationType,
-        type_option: &Option<Self::TypeOptionType>,
+        _configuration: &Self::ConfigurationType,
+        _type_option: &Option<Self::TypeOptionType>,
     ) -> Vec<Group> {
         let check_group = Group::new(
             "true".to_string(),
diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/mod.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/mod.rs
index a2f393d093..0d7b8fa03e 100644
--- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/mod.rs
+++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/mod.rs
@@ -4,3 +4,4 @@ mod util;
 
 pub use multi_select_controller::*;
 pub use single_select_controller::*;
+pub use util::*;
diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs
index 33cd0613cd..e2e7f13ef9 100644
--- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs
+++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs
@@ -3,9 +3,9 @@ use crate::services::cell::insert_select_option_cell;
 use crate::services::field::{MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser};
 use crate::services::group::action::GroupAction;
 
+use crate::services::group::controller::{GenericGroupController, GroupController, GroupGenerator};
 use crate::services::group::controller_impls::select_option_controller::util::*;
 use crate::services::group::entities::Group;
-use crate::services::group::group_controller::{GenericGroupController, GroupController, GroupGenerator};
 use flowy_grid_data_model::revision::{
     FieldRevision, RowChangeset, RowRevision, SelectOptionGroupConfigurationRevision,
 };
@@ -88,7 +88,7 @@ impl GroupGenerator for MultiSelectGroupGenerator {
     type TypeOptionType = MultiSelectTypeOptionPB;
     fn generate_groups(
         field_id: &str,
-        configuration: &Self::ConfigurationType,
+        _configuration: &Self::ConfigurationType,
         type_option: &Option<Self::TypeOptionType>,
     ) -> Vec<Group> {
         match type_option {
diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs
index 4c8360f427..44920a0f1f 100644
--- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs
+++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs
@@ -3,9 +3,9 @@ use crate::services::cell::insert_select_option_cell;
 use crate::services::field::{SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB};
 use crate::services::group::action::GroupAction;
 
+use crate::services::group::controller::{GenericGroupController, GroupController, GroupGenerator};
 use crate::services::group::controller_impls::select_option_controller::util::*;
 use crate::services::group::entities::Group;
-use crate::services::group::group_controller::{GenericGroupController, GroupController, GroupGenerator};
 
 use flowy_grid_data_model::revision::{
     FieldRevision, RowChangeset, RowRevision, SelectOptionGroupConfigurationRevision,
@@ -89,7 +89,7 @@ impl GroupGenerator for SingleSelectGroupGenerator {
     type TypeOptionType = SingleSelectTypeOptionPB;
     fn generate_groups(
         field_id: &str,
-        configuration: &Self::ConfigurationType,
+        _configuration: &Self::ConfigurationType,
         type_option: &Option<Self::TypeOptionType>,
     ) -> Vec<Group> {
         match type_option {
diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs
index b6439f1f12..6675f26aa4 100644
--- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs
+++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs
@@ -1,33 +1,14 @@
 use crate::entities::{GroupRowsChangesetPB, InsertedRowPB, RowPB};
 use crate::services::cell::insert_select_option_cell;
 use crate::services::field::SelectOptionCellDataPB;
-use crate::services::group::configuration::{GenericGroupConfiguration, GroupConfigurationAction};
+use crate::services::group::configuration::GenericGroupConfiguration;
 use crate::services::group::Group;
-use flowy_error::FlowyResult;
+
 use flowy_grid_data_model::revision::{
-    FieldRevision, GroupRecordRevision, RowChangeset, RowRevision, SelectOptionGroupConfigurationRevision,
+    FieldRevision, RowChangeset, RowRevision, SelectOptionGroupConfigurationRevision,
 };
 
 pub type SelectOptionGroupConfiguration = GenericGroupConfiguration<SelectOptionGroupConfigurationRevision>;
-impl GroupConfigurationAction for SelectOptionGroupConfiguration {
-    fn group_records(&self) -> &[GroupRecordRevision] {
-        // self.configuration.as_slice()
-        todo!()
-    }
-
-    fn merge_groups(&self, groups: Vec<Group>) -> FlowyResult<()> {
-        // self.writer.save_group_configuration()
-        todo!()
-    }
-
-    fn hide_group(&self, group_id: &str) -> FlowyResult<()> {
-        todo!()
-    }
-
-    fn show_group(&self, group_id: &str) -> FlowyResult<()> {
-        todo!()
-    }
-}
 
 pub fn add_row(
     group: &mut Group,
diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs
index 0b60b54535..7cd4e387b9 100644
--- a/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs
+++ b/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs
@@ -1,7 +1,8 @@
 use crate::services::group::configuration::GroupConfigurationReader;
-use crate::services::group::group_controller::GroupController;
+use crate::services::group::controller::GroupController;
 use crate::services::group::{
-    CheckboxGroupController, Group, GroupConfigurationWriter, MultiSelectGroupController, SingleSelectGroupController,
+    CheckboxGroupConfiguration, CheckboxGroupController, Group, GroupConfigurationWriter, MultiSelectGroupController,
+    SelectOptionGroupConfiguration, SingleSelectGroupController,
 };
 use flowy_error::FlowyResult;
 use flowy_grid_data_model::revision::{
@@ -35,8 +36,8 @@ impl GroupService {
     }
 
     pub(crate) async fn groups(&self) -> Vec<Group> {
-        // if let Some(group_action_handler) = self.group_controller.as_ref() {
-        //     group_action_handler.read().await.groups()
+        // if let Some(group_controller) = self.group_controller.as_ref() {
+        //     group_controller.read().await.groups()
         // } else {
         //     vec![]
         // }
@@ -182,30 +183,33 @@ impl GroupService {
                 // let generator = GroupGenerator::<DateGroupConfigurationPB>::from_configuration(configuration);
             }
             FieldType::SingleSelect => {
-                let controller = SingleSelectGroupController::new(
-                    field_rev,
+                let configuration = SelectOptionGroupConfiguration::new(
+                    field_rev.clone(),
                     self.configuration_reader.clone(),
                     self.configuration_writer.clone(),
                 )
                 .await?;
+                let controller = SingleSelectGroupController::new(field_rev, configuration).await?;
                 group_controller = Some(Arc::new(RwLock::new(controller)));
             }
             FieldType::MultiSelect => {
-                let controller = MultiSelectGroupController::new(
-                    field_rev,
+                let configuration = SelectOptionGroupConfiguration::new(
+                    field_rev.clone(),
                     self.configuration_reader.clone(),
                     self.configuration_writer.clone(),
                 )
                 .await?;
+                let controller = MultiSelectGroupController::new(field_rev, configuration).await?;
                 group_controller = Some(Arc::new(RwLock::new(controller)));
             }
             FieldType::Checkbox => {
-                let controller = CheckboxGroupController::new(
-                    field_rev,
+                let configuration = CheckboxGroupConfiguration::new(
+                    field_rev.clone(),
                     self.configuration_reader.clone(),
                     self.configuration_writer.clone(),
                 )
                 .await?;
+                let controller = CheckboxGroupController::new(field_rev, configuration).await?;
                 group_controller = Some(Arc::new(RwLock::new(controller)))
             }
             FieldType::URL => {
@@ -229,7 +233,7 @@ fn find_group_field(field_revs: &[Arc<FieldRevision>]) -> Option<Arc<FieldRevisi
 
 pub fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision {
     let field_id = field_rev.id.clone();
-    let field_type_rev = field_rev.ty.clone();
+    let field_type_rev = field_rev.ty;
     let field_type: FieldType = field_rev.ty.into();
     match field_type {
         FieldType::RichText => {
diff --git a/frontend/rust-lib/flowy-grid/src/services/group/mod.rs b/frontend/rust-lib/flowy-grid/src/services/group/mod.rs
index 19536b8c5b..2bc979c28d 100644
--- a/frontend/rust-lib/flowy-grid/src/services/group/mod.rs
+++ b/frontend/rust-lib/flowy-grid/src/services/group/mod.rs
@@ -1,8 +1,8 @@
 mod action;
 mod configuration;
+mod controller;
 mod controller_impls;
 mod entities;
-mod group_controller;
 mod group_service;
 
 pub(crate) use configuration::*;
diff --git a/frontend/rust-lib/flowy-grid/tests/grid/field_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/field_test/script.rs
index ebe3d3adf2..c790285fb1 100644
--- a/frontend/rust-lib/flowy-grid/tests/grid/field_test/script.rs
+++ b/frontend/rust-lib/flowy-grid/tests/grid/field_test/script.rs
@@ -1,7 +1,6 @@
 use crate::grid::grid_editor::GridEditorTest;
-use flowy_grid::entities::InsertFieldParams;
+use flowy_grid::entities::{FieldChangesetParams, InsertFieldParams};
 use flowy_grid_data_model::revision::FieldRevision;
-use flowy_sync::entities::grid::FieldChangesetParams;
 
 pub enum FieldScript {
     CreateField {
diff --git a/frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs b/frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs
index 98cb3d0324..3e255d3662 100644
--- a/frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs
+++ b/frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs
@@ -1,10 +1,10 @@
 use crate::grid::field_test::script::FieldScript::*;
 use crate::grid::field_test::script::GridFieldTest;
 use crate::grid::field_test::util::*;
+use flowy_grid::entities::FieldChangesetParams;
 use flowy_grid::services::field::selection_type_option::SelectOptionPB;
 use flowy_grid::services::field::SingleSelectTypeOptionPB;
 use flowy_grid_data_model::revision::TypeOptionDataEntry;
-use flowy_sync::entities::grid::FieldChangesetParams;
 
 #[tokio::test]
 async fn grid_create_field() {
diff --git a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs
index 459cf5f82d..948528589f 100644
--- a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs
+++ b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs
@@ -3,10 +3,9 @@
 #![allow(dead_code)]
 #![allow(unused_imports)]
 
-use flowy_grid::entities::{CreateGridFilterPayloadPB, GridLayout, GridSettingPB};
+use flowy_grid::entities::{CreateFilterParams, CreateGridFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB};
 use flowy_grid::services::setting::GridSettingChangesetBuilder;
 use flowy_grid_data_model::revision::{FieldRevision, FieldTypeRevision};
-use flowy_sync::entities::grid::{CreateFilterParams, DeleteFilterParams, GridSettingChangesetParams};
 use crate::grid::grid_editor::GridEditorTest;
 
 pub enum FilterScript {
diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs
index ea8de69056..78cfc17b26 100644
--- a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs
+++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs
@@ -12,9 +12,6 @@ use flowy_grid::services::setting::GridSettingChangesetBuilder;
 use flowy_grid_data_model::revision::*;
 use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;
 use flowy_sync::client_grid::GridBuilder;
-use flowy_sync::entities::grid::{
-    CreateFilterParams, DeleteFilterParams, FieldChangesetParams, GridSettingChangesetParams,
-};
 use flowy_test::helper::ViewTest;
 use flowy_test::FlowySDKTest;
 use std::collections::HashMap;
diff --git a/shared-lib/flowy-grid-data-model/src/revision/group_rev.rs b/shared-lib/flowy-grid-data-model/src/revision/group_rev.rs
index c4d2287c0c..9d7da79e5a 100644
--- a/shared-lib/flowy-grid-data-model/src/revision/group_rev.rs
+++ b/shared-lib/flowy-grid-data-model/src/revision/group_rev.rs
@@ -3,8 +3,21 @@ use serde::{Deserialize, Serialize};
 use serde_json::Error;
 use serde_repr::*;
 
-pub trait GroupConfigurationContentSerde: Sized {
+pub trait GroupConfigurationContent: Sized {
     fn from_configuration_content(s: &str) -> Result<Self, serde_json::Error>;
+
+    fn get_groups(&self) -> &[GroupRecordRevision] {
+        &[]
+    }
+
+    fn mut_group<F>(&mut self, _group_id: &str, _f: F)
+    where
+        F: FnOnce(&mut GroupRecordRevision),
+    {
+    }
+
+    fn set_groups(&mut self, _new_groups: Vec<GroupRecordRevision>) {}
+
     fn to_configuration_content(&self) -> Result<String, serde_json::Error>;
 }
 
@@ -36,7 +49,7 @@ pub struct TextGroupConfigurationRevision {
     pub hide_empty: bool,
 }
 
-impl GroupConfigurationContentSerde for TextGroupConfigurationRevision {
+impl GroupConfigurationContent for TextGroupConfigurationRevision {
     fn from_configuration_content(s: &str) -> Result<Self, Error> {
         serde_json::from_str(s)
     }
@@ -50,7 +63,7 @@ pub struct NumberGroupConfigurationRevision {
     pub hide_empty: bool,
 }
 
-impl GroupConfigurationContentSerde for NumberGroupConfigurationRevision {
+impl GroupConfigurationContent for NumberGroupConfigurationRevision {
     fn from_configuration_content(s: &str) -> Result<Self, Error> {
         serde_json::from_str(s)
     }
@@ -64,7 +77,7 @@ pub struct UrlGroupConfigurationRevision {
     pub hide_empty: bool,
 }
 
-impl GroupConfigurationContentSerde for UrlGroupConfigurationRevision {
+impl GroupConfigurationContent for UrlGroupConfigurationRevision {
     fn from_configuration_content(s: &str) -> Result<Self, Error> {
         serde_json::from_str(s)
     }
@@ -78,7 +91,7 @@ pub struct CheckboxGroupConfigurationRevision {
     pub hide_empty: bool,
 }
 
-impl GroupConfigurationContentSerde for CheckboxGroupConfigurationRevision {
+impl GroupConfigurationContent for CheckboxGroupConfigurationRevision {
     fn from_configuration_content(s: &str) -> Result<Self, Error> {
         serde_json::from_str(s)
     }
@@ -94,17 +107,35 @@ pub struct SelectOptionGroupConfigurationRevision {
     pub groups: Vec<GroupRecordRevision>,
 }
 
-impl GroupConfigurationContentSerde for SelectOptionGroupConfigurationRevision {
+impl GroupConfigurationContent for SelectOptionGroupConfigurationRevision {
     fn from_configuration_content(s: &str) -> Result<Self, Error> {
         serde_json::from_str(s)
     }
 
+    fn get_groups(&self) -> &[GroupRecordRevision] {
+        &self.groups
+    }
+
+    fn mut_group<F>(&mut self, group_id: &str, f: F)
+    where
+        F: FnOnce(&mut GroupRecordRevision),
+    {
+        match self.groups.iter_mut().find(|group| group.group_id == group_id) {
+            None => {}
+            Some(group) => f(group),
+        }
+    }
+
+    fn set_groups(&mut self, new_groups: Vec<GroupRecordRevision>) {
+        self.groups = new_groups;
+    }
+
     fn to_configuration_content(&self) -> Result<String, Error> {
         serde_json::to_string(self)
     }
 }
 
-#[derive(Default, Serialize, Deserialize)]
+#[derive(Clone, Default, Serialize, Deserialize)]
 pub struct GroupRecordRevision {
     pub group_id: String,
 
@@ -113,13 +144,22 @@ pub struct GroupRecordRevision {
 }
 const DEFAULT_GROUP_RECORD_VISIBILITY: fn() -> bool = || true;
 
+impl GroupRecordRevision {
+    pub fn new(group_id: String) -> Self {
+        Self {
+            group_id,
+            visible: true,
+        }
+    }
+}
+
 #[derive(Default, Serialize, Deserialize)]
 pub struct DateGroupConfigurationRevision {
     pub hide_empty: bool,
     pub condition: DateCondition,
 }
 
-impl GroupConfigurationContentSerde for DateGroupConfigurationRevision {
+impl GroupConfigurationContent for DateGroupConfigurationRevision {
     fn from_configuration_content(s: &str) -> Result<Self, Error> {
         serde_json::from_str(s)
     }
diff --git a/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs b/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs
index 3f5f799369..fc5fa4bf67 100644
--- a/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs
+++ b/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs
@@ -70,15 +70,15 @@ impl GridViewRevisionPad {
         &mut self,
         field_id: &str,
         field_type: &FieldTypeRevision,
-        group_id: &str,
-        mut_group_fn: F,
+        configuration_id: &str,
+        mut_configuration_fn: F,
     ) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
         self.modify(|view| match view.groups.get_mut_objects(field_id, field_type) {
             None => Ok(None),
-            Some(groups) => {
-                for mut group in groups {
-                    if group.id == group_id {
-                        mut_group_fn(Arc::make_mut(&mut group));
+            Some(configurations_revs) => {
+                for configuration_rev in configurations_revs {
+                    if configuration_rev.id == configuration_id {
+                        mut_configuration_fn(Arc::make_mut(configuration_rev));
                         return Ok(Some(()));
                     }
                 }
@@ -170,6 +170,7 @@ impl GridViewRevisionPad {
     }
 }
 
+#[derive(Debug)]
 pub struct GridViewRevisionChangeset {
     pub delta: TextDelta,
     pub md5: String,