Skip to content

feat!: cleanup evaluator interface#1793

Merged
toddbaert merged 2 commits into
open-feature:mainfrom
open-feature-forking:refactor/cleanup_ievaluator_interface
Sep 8, 2025
Merged

feat!: cleanup evaluator interface#1793
toddbaert merged 2 commits into
open-feature:mainfrom
open-feature-forking:refactor/cleanup_ievaluator_interface

Conversation

@aepfli

@aepfli aepfli commented Sep 4, 2025

Copy link
Copy Markdown
Member

depends: #1792 for fixing build issues

@aepfli aepfli requested review from a team as code owners September 4, 2025 08:41
@netlify

netlify Bot commented Sep 4, 2025

Copy link
Copy Markdown

Deploy Preview for polite-licorice-3db33c canceled.

Name Link
🔨 Latest commit 41c8b62
🔍 Latest deploy log https://app.netlify.com/projects/polite-licorice-3db33c/deploys/68bee7bf0dce7e00082b353d

@dosubot dosubot Bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Sep 4, 2025

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Summary of Changes

Hello @aepfli, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request primarily focuses on streamlining the evaluator and store interfaces by simplifying their state management methods. The changes aim to reduce complexity and clarify responsibilities, moving away from direct state retrieval and explicit notification returns at these interface levels. The modifications are propagated throughout the codebase, including tests and mock implementations, to ensure consistency.

Highlights

  • Evaluator Interface Simplification: The IEvaluator interface has been refactored to remove the GetState() method and simplify the SetState() method signature. SetState() now returns only an error, removing previous returns for changed flags and resync requirements.
  • Store Interface Alignment: The IStore interface and its Store implementation have been updated to align with the evaluator changes. The String() method has been removed, and the Update() method no longer returns notification or resync information.
  • Test Suite Updates: All relevant test files (json_test.go, store_test.go) and mock implementations (ievaluator.go, http.go) have been updated to reflect the new interface signatures and removed functionalities, ensuring compatibility and correctness.
  • New Mock Interface: A new MockCron interface and its mock recorder have been added in core/pkg/sync/http/mock/http.go to facilitate testing of cron-related functionalities.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@aepfli aepfli changed the title refact: cleanup evaluator interface feat!: cleanup evaluator interface Sep 4, 2025

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request is a good refactoring that simplifies the IEvaluator and IStore interfaces by removing event and resync notifications from the state update methods. The changes are consistent across the codebase, and the tests have been updated accordingly, with some tests being significantly improved. I have a couple of suggestions to enhance observability and fix a bug in a test.

Comment thread core/pkg/evaluator/json_test.go Outdated
Comment on lines +110 to +138
je.store.Update(payload.Source, definition.Flags, definition.Metadata)

events, reSync = je.store.Update(payload.Source, definition.Flags, definition.Metadata)

// Number of events correlates to the number of flags changed through this sync, record it
span.SetAttributes(attribute.Int("feature_flag.change_count", len(events)))

return events, reSync, nil
return nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This change removes the feature_flag.change_count attribute from the flagSync trace span, which is a regression in observability. This metric is valuable for understanding the impact of flag updates. Consider reintroducing it. This would likely require modifying IStore.Update to return the number of changes.

    changeCount := je.store.Update(payload.Source, definition.Flags, definition.Metadata)

	// Number of events correlates to the number of flags changed through this sync, record it
	span.SetAttributes(attribute.Int("feature_flag.change_count", changeCount))

	return nil

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

removed on purpose, we need to see how we want to handle this information in the future

Comment thread core/pkg/store/store.go
@aepfli aepfli force-pushed the refactor/cleanup_ievaluator_interface branch from 77bccf1 to a4b9efc Compare September 8, 2025 14:26
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. labels Sep 8, 2025
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>

diff --git c/core/pkg/evaluator/ievaluator.go i/core/pkg/evaluator/ievaluator.go
index 9f492c2..00b5590 100644
--- c/core/pkg/evaluator/ievaluator.go
+++ i/core/pkg/evaluator/ievaluator.go
@@ -34,8 +34,7 @@ func NewAnyValue(
 IEvaluator is an extension of IResolver, allowing storage updates and retrievals
 */
 type IEvaluator interface {
-	GetState() (string, error)
-	SetState(payload sync.DataSync) (map[string]interface{}, bool, error)
+	SetState(payload sync.DataSync) error
 	IResolver
 }

diff --git c/core/pkg/evaluator/json.go i/core/pkg/evaluator/json.go
index 432b4a7..12b862b 100644
--- c/core/pkg/evaluator/json.go
+++ i/core/pkg/evaluator/json.go
@@ -117,15 +117,7 @@ func NewJSON(logger *logger.Logger, s store.IStore, opts ...JSONEvaluatorOption)
 	return &ev
 }

-func (je *JSON) GetState() (string, error) {
-	s, err := je.store.String()
-	if err != nil {
-		return "", fmt.Errorf("unable to fetch evaluator state: %w", err)
-	}
-	return s, nil
-}
-
-func (je *JSON) SetState(payload sync.DataSync) (map[string]interface{}, bool, error) {
+func (je *JSON) SetState(payload sync.DataSync) error {
 	_, span := je.jsonEvalTracer.Start(
 		context.Background(),
 		"flagSync",
@@ -138,18 +130,12 @@ func (je *JSON) SetState(payload sync.DataSync) (map[string]interface{}, bool, e
 	if err != nil {
 		span.SetStatus(codes.Error, "flagSync error")
 		span.RecordError(err)
-		return nil, false, err
+		return err
 	}

-	var events map[string]interface{}
-	var reSync bool
+	je.store.Update(payload.Source, definition.Flags, definition.Metadata)

-	events, reSync = je.store.Update(payload.Source, definition.Flags, definition.Metadata)
-
-	// Number of events correlates to the number of flags changed through this sync, record it
-	span.SetAttributes(attribute.Int("feature_flag.change_count", len(events)))
-
-	return events, reSync, nil
+	return nil
 }

 // Resolver implementation for flagd flags. This resolver should be kept reusable, hence must interact with interfaces.
diff --git c/core/pkg/evaluator/json_test.go i/core/pkg/evaluator/json_test.go
index 540b299..1e68def 100644
--- c/core/pkg/evaluator/json_test.go
+++ i/core/pkg/evaluator/json_test.go
@@ -1,15 +1,15 @@
 //nolint:wrapcheck
-package evaluator
+package evaluator_test

 import (
 	"context"
 	"encoding/json"
 	"fmt"
 	"reflect"
-	"strings"
 	"testing"
 	"time"

+	"github.com/open-feature/flagd/core/pkg/evaluator"
 	"github.com/open-feature/flagd/core/pkg/logger"
 	"github.com/open-feature/flagd/core/pkg/model"
 	"github.com/open-feature/flagd/core/pkg/store"
@@ -381,48 +381,42 @@ var flagConfig = fmt.Sprintf(`{
 	VersionOverride)

 func TestGetState_Valid_ContainsFlag(t *testing.T) {
-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: ValidFlags, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: ValidFlags, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("Expected no error")
 	}

 	// get the state
-	state, err := evaluator.GetState()
-	if err != nil {
+	state := evaluator.ResolveAsAnyValue(context.Background(), "", "validFlag", nil)
+	if state.Error != nil {
 		t.Fatalf("expected no error")
 	}
-
-	// validate it contains the flag
-	wants := "validFlag"
-	if !strings.Contains(state, wants) {
-		t.Fatalf("expected: %s to contain: %s", state, wants)
-	}
 }

 func TestSetState_Invalid_Error(t *testing.T) {
-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

 	// set state with an invalid flag definition
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: InvalidFlags, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: InvalidFlags, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("unexpected error")
 	}
 }

 func TestSetState_Valid_NoError(t *testing.T) {
-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

 	// set state with a valid flag definition
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: ValidFlags, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: ValidFlags, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
 }

 func TestResolveAllValues(t *testing.T) {
-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -490,8 +484,8 @@ func TestResolveBooleanValue(t *testing.T) {
 		{DisabledFlag, nil, StaticBoolValue, model.ErrorReason, model.FlagDisabledErrorCode},
 	}
 	const reqID = "default"
-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -525,8 +519,8 @@ func BenchmarkResolveBooleanValue(b *testing.B) {
 		{DisabledFlag, nil, StaticBoolValue, model.ErrorReason, model.FlagDisabledErrorCode},
 	}

-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		b.Fatalf("expected no error")
 	}
@@ -565,8 +559,8 @@ func TestResolveStringValue(t *testing.T) {
 		{DisabledFlag, nil, "", model.ErrorReason, model.FlagDisabledErrorCode},
 	}
 	const reqID = "default"
-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -601,8 +595,8 @@ func BenchmarkResolveStringValue(b *testing.B) {
 		{DisabledFlag, nil, "", model.ErrorReason, model.FlagDisabledErrorCode},
 	}

-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		b.Fatalf("expected no error")
 	}
@@ -641,8 +635,8 @@ func TestResolveFloatValue(t *testing.T) {
 		{DisabledFlag, nil, 0, model.ErrorReason, model.FlagDisabledErrorCode},
 	}
 	const reqID = "default"
-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -677,8 +671,8 @@ func BenchmarkResolveFloatValue(b *testing.B) {
 		{DisabledFlag, nil, 0, model.ErrorReason, model.FlagDisabledErrorCode},
 	}

-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		b.Fatalf("expected no error")
 	}
@@ -717,8 +711,8 @@ func TestResolveIntValue(t *testing.T) {
 		{DisabledFlag, nil, 0, model.ErrorReason, model.FlagDisabledErrorCode},
 	}
 	const reqID = "default"
-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -753,8 +747,8 @@ func BenchmarkResolveIntValue(b *testing.B) {
 		{DisabledFlag, nil, 0, model.ErrorReason, model.FlagDisabledErrorCode},
 	}

-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		b.Fatalf("expected no error")
 	}
@@ -793,8 +787,8 @@ func TestResolveObjectValue(t *testing.T) {
 		{DisabledFlag, nil, "{}", model.ErrorReason, model.FlagDisabledErrorCode},
 	}
 	const reqID = "default"
-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -832,8 +826,8 @@ func BenchmarkResolveObjectValue(b *testing.B) {
 		{DisabledFlag, nil, "{}", model.ErrorReason, model.FlagDisabledErrorCode},
 	}

-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		b.Fatalf("expected no error")
 	}
@@ -877,8 +871,8 @@ func TestResolveAsAnyValue(t *testing.T) {
 		{DisabledFlag, nil, "{}", model.ErrorReason, model.FlagDisabledErrorCode},
 	}

-	evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -913,8 +907,8 @@ func TestResolve_DefaultVariant(t *testing.T) {

 	for _, test := range tests {
 		t.Run("", func(t *testing.T) {
-			evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-			_, _, err := evaluator.SetState(sync.DataSync{FlagData: test.flags, Source: "testSource"})
+			evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+			err := evaluator.SetState(sync.DataSync{FlagData: test.flags, Source: "testSource"})

 			if err != nil {
 				t.Fatalf("expected no error")
@@ -979,9 +973,9 @@ func TestSetState_DefaultVariantValidation(t *testing.T) {

 	for name, tt := range tests {
 		t.Run(name, func(t *testing.T) {
-			jsonEvaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+			jsonEvaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-			_, _, err := jsonEvaluator.SetState(sync.DataSync{FlagData: tt.jsonFlags, Source: "testSource"})
+			err := jsonEvaluator.SetState(sync.DataSync{FlagData: tt.jsonFlags, Source: "testSource"})

 			if tt.valid && err != nil {
 				t.Error(err)
@@ -993,7 +987,7 @@ func TestSetState_DefaultVariantValidation(t *testing.T) {
 func TestState_Evaluator(t *testing.T) {
 	tests := map[string]struct {
 		inputState          string
-		expectedOutputState string
+		expectedOutputState map[string]model.Flag
 		expectedError       bool
 		expectedResync      bool
 	}{
@@ -1010,6 +1004,9 @@ func TestState_Evaluator(t *testing.T) {
 						  },
 						  "defaultVariant": "recursive",
 						  "state": "ENABLED",
+						  "metadata": {
+						    "flagSetId": "flagSetId"
+						  },
 						  "targeting": {
 							"if": [
 							  {
@@ -1028,20 +1025,19 @@ func TestState_Evaluator(t *testing.T) {
   					}
 				}
 			`,
-			expectedOutputState: `
-				{
-  					"flags": {
-						"fibAlgo": {
-						  "variants": {
-							"recursive": "recursive",
-							"memo": "memo",
-							"loop": "loop",
-							"binet": "binet"
-						  },
-						  "defaultVariant": "recursive",
-						  "state": "ENABLED",
-						  "source":"testSource",
-						  "targeting": {
+			expectedOutputState: map[string]model.Flag{
+				"fibAlgo": {
+					Key: "fibAlgo",
+					Variants: map[string]any{
+						"recursive": "recursive",
+						"memo":      "memo",
+						"loop":      "loop",
+						"binet":     "binet",
+					},
+					DefaultVariant: "recursive",
+					State:          "ENABLED",
+					Source:         "testSource",
+					Targeting: json.RawMessage(`{
 							"if": [
 							  {
 								"in": ["@faas.com", {
@@ -1049,12 +1045,13 @@ func TestState_Evaluator(t *testing.T) {
 							  }]
 							  }, "binet", null
 							]
-						  }
-    					}
+						  }`),
+					Metadata: map[string]interface{}{
+						"flagSetId": "flagSetId",
 					},
-					"flagSources":null
-				}
-			`,
+					FlagSetId: "flagSetId",
+				},
+			},
 		},
 		"no-indentation": {
 			inputState: `
@@ -1069,6 +1066,9 @@ func TestState_Evaluator(t *testing.T) {
 				},
 				"defaultVariant": "recursive",
 				"state": "ENABLED",
+				"metadata": {
+				  "flagSetId": "flagSetId"
+				},
 				"targeting": {
 				"if": [
 				{
@@ -1087,20 +1087,19 @@ func TestState_Evaluator(t *testing.T) {
 				}
 				}
 			`,
-			expectedOutputState: `
-				{
-  					"flags": {
-						"fibAlgo": {
-						  "variants": {
-							"recursive": "recursive",
-							"memo": "memo",
-							"loop": "loop",
-							"binet": "binet"
-						  },
-						  "defaultVariant": "recursive",
-						  "state": "ENABLED",
-						  "source":"testSource",
-						  "targeting": {
+			expectedOutputState: map[string]model.Flag{
+				"fibAlgo": {
+					Key: "fibAlgo",
+					Variants: map[string]any{
+						"recursive": "recursive",
+						"memo":      "memo",
+						"loop":      "loop",
+						"binet":     "binet",
+					},
+					DefaultVariant: "recursive",
+					State:          "ENABLED",
+					Source:         "testSource",
+					Targeting: json.RawMessage(`{
 							"if": [
 							  {
 								"in": ["@faas.com", {
@@ -1108,12 +1107,13 @@ func TestState_Evaluator(t *testing.T) {
 							  }]
 							  }, "binet", null
 							]
-						  }
-    					}
+						  }`),
+					Metadata: map[string]interface{}{
+						"flagSetId": "flagSetId",
 					},
-					"flagSources":null
-				}
-			`,
+					FlagSetId: "flagSetId",
+				},
+			},
 		},
 		"invalid evaluator json": {
 			inputState: `
@@ -1128,6 +1128,9 @@ func TestState_Evaluator(t *testing.T) {
 						  },
 						  "defaultVariant": "recursive",
 						  "state": "ENABLED",
+						  "metadata": {
+						    "flagSetId": "flagSetId"
+						  },
 						  "targeting": {
 							"if": [
 							  {
@@ -1157,6 +1160,9 @@ func TestState_Evaluator(t *testing.T) {
 						},
 						"defaultVariant": "recursive",
 						"state": "ENABLED",
+						"metadata": {
+						  "flagSetId": "flagSetId"
+						},
 						"targeting": {
 						"if": [
 							{
@@ -1175,6 +1181,9 @@ func TestState_Evaluator(t *testing.T) {
 						},
 						"defaultVariant": "off",
 						"source":"testSource",
+						"metadata": {
+						  "flagSetId": "flagSetId"
+						},
 						"targeting": {
 							"if": [
 								{
@@ -1196,57 +1205,55 @@ func TestState_Evaluator(t *testing.T) {
 			}
 		`,
 			expectedError: false,
-			expectedOutputState: `
-			{
-					"flags": {
-					"fibAlgo": {
-						"variants": {
+			expectedOutputState: map[string]model.Flag{
+				"fibAlgo": {
+					Key: "fibAlgo",
+					Variants: map[string]any{
 						"recursive": "recursive",
-						"memo": "memo",
-						"loop": "loop",
-						"binet": "binet"
-						},
-						"defaultVariant": "recursive",
-						"state": "ENABLED",
-						"source":"testSource",
-						"targeting": {
-						"if": [
-							{
-							"in": ["@faas.com", {
-							"var": ["email"]
-							}]
-							}, "binet", "null", "loop"
-						]
-						}
-						},
-					"isColorYellow": {
-						"state": "ENABLED",
-						"variants": {
-							"on": true,
-							"off": false
-						},
-						"defaultVariant": "off",
-						"source":"testSource",
-						"targeting": {
+						"memo":      "memo",
+						"loop":      "loop",
+						"binet":     "binet",
+					},
+					DefaultVariant: "recursive",
+					State:          "ENABLED",
+					Source:         "testSource",
+					Targeting: json.RawMessage(`{
+							"if": [
+							  {
+								"in": ["@faas.com", {
+								"var": ["email"]
+							  }]
+							  }, "binet", null
+							]
+						  }`),
+					Metadata: map[string]interface{}{
+						"flagSetId": "flagSetId",
+					},
+					FlagSetId: "flagSetId",
+				},
+				"isColorYellow": {
+					Key: "isColorYellow",
+					Variants: map[string]any{
+						"on":  true,
+						"off": false,
+					},
+					DefaultVariant: "off",
+					State:          "ENABLED",
+					Source:         "testSource",
+					Targeting: json.RawMessage(`{
 							"if": [
 								{
 									"==": [
-										{
-											"varr": ["color"]
-										},
-										"yellow"
-									]
-								},
-								"on",
 								"off",
 								"none"
 							]
-						}
-					}
+						}`),
+					Metadata: map[string]interface{}{
+						"flagSetId": "flagSetId",
+					},
+					FlagSetId: "flagSetId",
 				},
-				"flagSources":null
-			}
-		`,
+			},
 		},
 		"empty evaluator": {
 			inputState: `
@@ -1281,54 +1288,63 @@ func TestState_Evaluator(t *testing.T) {

 	for name, tt := range tests {
 		t.Run(name, func(t *testing.T) {
-			jsonEvaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+			jsonEvaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-			_, resync, err := jsonEvaluator.SetState(sync.DataSync{FlagData: tt.inputState, Source: "testSource"})
+			err := jsonEvaluator.SetState(sync.DataSync{FlagData: tt.inputState, Source: "testSource"})
 			if err != nil {
 				if !tt.expectedError {
 					t.Error(err)
 				}
-				if resync != tt.expectedResync {
-					t.Errorf("expected resync %t got %t", tt.expectedResync, resync)
-				}
 				return
 			} else if tt.expectedError {
 				t.Error("expected error, got nil")
 				return
 			}

-			if resync != tt.expectedResync {
-				t.Errorf("expected resync %t got %t", tt.expectedResync, resync)
-			}
-
-			got, err := jsonEvaluator.GetState()
+			got, _, err := jsonEvaluator.store.GetAll(context.Background(), nil)
 			if err != nil {
 				t.Error(err)
 			}

-			var expectedOutputJSON map[string]interface{}
-			if err := json.Unmarshal([]byte(tt.expectedOutputState), &expectedOutputJSON); err != nil {
-				t.Fatal(err)
-			}
+			for _, v := range got {

-			var gotOutputJSON map[string]interface{}
-			if err := json.Unmarshal([]byte(got), &gotOutputJSON); err != nil {
-				t.Fatal(err)
-			}
+				// json data can be formatted differently, hence we remove it from the object and compare separately
+				parsedTargeting, _ := normalizeJSON(v.Targeting)
+				flag := tt.expectedOutputState[v.Key]
+				flag.Targeting = nil
+				gotTargeting, _ := normalizeJSON(v.Targeting)
+				v.Targeting = nil

-			if !reflect.DeepEqual(expectedOutputJSON["flags"], gotOutputJSON) {
-				t.Errorf("expected state: %v got state: %v", expectedOutputJSON["flags"], gotOutputJSON)
+				if !reflect.DeepEqual(parsedTargeting, gotTargeting) {
+					t.Errorf("\nexpected targeting: %s\ngot targeting: %s", parsedTargeting, gotTargeting)
+				}
+
+				if !reflect.DeepEqual(flag, v) {
+					t.Errorf("expected state: %v got state: %v", flag, v)
+				}
 			}
 		})
 	}
 }

+func normalizeJSON(jsonData []byte) (interface{}, error) {
+	var result interface{}
+	if jsonData == nil {
+		return nil, nil // Handle nil gracefully
+	}
+	err := json.Unmarshal(jsonData, &result)
+	if err != nil {
+		return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
+	}
+	return result, nil
+}
+
 func TestFlagStateSafeForConcurrentReadWrites(t *testing.T) {
 	tests := map[string]struct {
-		flagResolution func(evaluator *JSON) error
+		flagResolution func(evaluator *evaluator.JSON) error
 	}{
 		"Add_ResolveAllValues": {
-			flagResolution: func(evaluator *JSON) error {
+			flagResolution: func(evaluator *evaluator.JSON) error {
 				_, _, err := evaluator.ResolveAllValues(context.TODO(), "", nil)
 				if err != nil {
 					return err
@@ -1337,7 +1353,7 @@ func TestFlagStateSafeForConcurrentReadWrites(t *testing.T) {
 			},
 		},
 		"Update_ResolveAllValues": {
-			flagResolution: func(evaluator *JSON) error {
+			flagResolution: func(evaluator *evaluator.JSON) error {
 				_, _, err := evaluator.ResolveAllValues(context.TODO(), "", nil)
 				if err != nil {
 					return err
@@ -1346,7 +1362,7 @@ func TestFlagStateSafeForConcurrentReadWrites(t *testing.T) {
 			},
 		},
 		"Delete_ResolveAllValues": {
-			flagResolution: func(evaluator *JSON) error {
+			flagResolution: func(evaluator *evaluator.JSON) error {
 				_, _, err := evaluator.ResolveAllValues(context.TODO(), "", nil)
 				if err != nil {
 					return err
@@ -1355,31 +1371,31 @@ func TestFlagStateSafeForConcurrentReadWrites(t *testing.T) {
 			},
 		},
 		"Add_ResolveBooleanValue": {
-			flagResolution: func(evaluator *JSON) error {
+			flagResolution: func(evaluator *evaluator.JSON) error {
 				_, _, _, _, err := evaluator.ResolveBooleanValue(context.TODO(), "", StaticBoolFlag, nil)
 				return err
 			},
 		},
 		"Update_ResolveStringValue": {
-			flagResolution: func(evaluator *JSON) error {
+			flagResolution: func(evaluator *evaluator.JSON) error {
 				_, _, _, _, err := evaluator.ResolveBooleanValue(context.TODO(), "", StaticStringValue, nil)
 				return err
 			},
 		},
 		"Delete_ResolveIntValue": {
-			flagResolution: func(evaluator *JSON) error {
+			flagResolution: func(evaluator *evaluator.JSON) error {
 				_, _, _, _, err := evaluator.ResolveIntValue(context.TODO(), "", StaticIntFlag, nil)
 				return err
 			},
 		},
 		"Add_ResolveFloatValue": {
-			flagResolution: func(evaluator *JSON) error {
+			flagResolution: func(evaluator *evaluator.JSON) error {
 				_, _, _, _, err := evaluator.ResolveFloatValue(context.TODO(), "", StaticFloatFlag, nil)
 				return err
 			},
 		},
 		"Update_ResolveObjectValue": {
-			flagResolution: func(evaluator *JSON) error {
+			flagResolution: func(evaluator *evaluator.JSON) error {
 				_, _, _, _, err := evaluator.ResolveObjectValue(context.TODO(), "", StaticObjectFlag, nil)
 				return err
 			},
@@ -1388,9 +1404,9 @@ func TestFlagStateSafeForConcurrentReadWrites(t *testing.T) {

 	for name, tt := range tests {
 		t.Run(name, func(t *testing.T) {
-			jsonEvaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+			jsonEvaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-			_, _, err := jsonEvaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+			err := jsonEvaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -1413,7 +1429,7 @@ func TestFlagStateSafeForConcurrentReadWrites(t *testing.T) {
 						errChan <- nil
 						return
 					default:
-						_, _, err := jsonEvaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+						err := jsonEvaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 						if err != nil {
 							errChan <- err
 							return
@@ -1453,9 +1469,9 @@ func TestFlagStateSafeForConcurrentReadWrites(t *testing.T) {

 func TestFlagdAmbientProperties(t *testing.T) {
 	t.Run("flagKeyIsInTheContext", func(t *testing.T) {
-		evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+		evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-		_, _, err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
+		err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
 			"flags": {
 				"welcome-banner": {
 					"state": "ENABLED",
@@ -1493,9 +1509,9 @@ func TestFlagdAmbientProperties(t *testing.T) {
 	})

 	t.Run("timestampIsInTheContext", func(t *testing.T) {
-		evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+		evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-		_, _, err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
+		err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
 			"flags": {
 				"welcome-banner": {
 					"state": "ENABLED",
@@ -1527,9 +1543,9 @@ func TestFlagdAmbientProperties(t *testing.T) {

 func TestTargetingVariantBehavior(t *testing.T) {
 	t.Run("missing variant error", func(t *testing.T) {
-		evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+		evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-		_, _, err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
+		err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
 			"flags": {
 				"missing-variant": {
 					"state": "ENABLED",
@@ -1555,9 +1571,9 @@ func TestTargetingVariantBehavior(t *testing.T) {
 	})

 	t.Run("null fallback", func(t *testing.T) {
-		evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+		evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-		_, _, err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
+		err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
 			"flags": {
 				"null-fallback": {
 					"state": "ENABLED",
@@ -1587,10 +1603,10 @@ func TestTargetingVariantBehavior(t *testing.T) {
 	})

 	t.Run("match booleans", func(t *testing.T) {
-		evaluator := NewJSON(logger.NewLogger(nil, false), store.NewFlags())
+		evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

 		//nolint:dupword
-		_, _, err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
+		err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
 			"flags": {
 				"match-boolean": {
 					"state": "ENABLED",
diff --git c/core/pkg/evaluator/mock/ievaluator.go i/core/pkg/evaluator/mock/ievaluator.go
index 98d0367..bf0ce56 100644
--- c/core/pkg/evaluator/mock/ievaluator.go
+++ i/core/pkg/evaluator/mock/ievaluator.go
@@ -42,21 +42,6 @@ func (m *MockIEvaluator) EXPECT() *MockIEvaluatorMockRecorder {
 	return m.recorder
 }

-// GetState mocks base method.
-func (m *MockIEvaluator) GetState() (string, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetState")
-	ret0, _ := ret[0].(string)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// GetState indicates an expected call of GetState.
-func (mr *MockIEvaluatorMockRecorder) GetState() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetState", reflect.TypeOf((*MockIEvaluator)(nil).GetState))
-}
-
 // ResolveAllValues mocks base method.
 func (m *MockIEvaluator) ResolveAllValues(ctx context.Context, reqID string, context map[string]any) ([]evaluator.AnyValue, model.Metadata, error) {
 	m.ctrl.T.Helper()
@@ -178,13 +163,11 @@ func (mr *MockIEvaluatorMockRecorder) ResolveStringValue(ctx, reqID, flagKey, co
 }

 // SetState mocks base method.
-func (m *MockIEvaluator) SetState(payload sync.DataSync) (map[string]any, bool, error) {
+func (m *MockIEvaluator) SetState(payload sync.DataSync) error {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "SetState", payload)
-	ret0, _ := ret[0].(map[string]any)
-	ret1, _ := ret[1].(bool)
-	ret2, _ := ret[2].(error)
-	return ret0, ret1, ret2
+	ret0, _ := ret[0].(error)
+	return ret0
 }

 // SetState indicates an expected call of SetState.
diff --git c/core/pkg/store/store.go i/core/pkg/store/store.go
index 2b8b87c..0c6bc16 100644
--- c/core/pkg/store/store.go
+++ i/core/pkg/store/store.go
@@ -2,14 +2,12 @@ package store

 import (
 	"context"
-	"encoding/json"
 	"fmt"
 	"slices"

 	"github.com/hashicorp/go-memdb"
 	"github.com/open-feature/flagd/core/pkg/logger"
 	"github.com/open-feature/flagd/core/pkg/model"
-	"github.com/open-feature/flagd/core/pkg/notifications"
 )

 var noValidatedSources = []string{}
@@ -24,8 +22,7 @@ type IStore interface {
 	Get(ctx context.Context, key string, selector *Selector) (model.Flag, model.Metadata, error)
 	GetAll(ctx context.Context, selector *Selector) (map[string]model.Flag, model.Metadata, error)
 	Watch(ctx context.Context, selector *Selector, watcher chan<- FlagQueryResult)
-	Update(source string, flags map[string]model.Flag, metadata model.Metadata) (map[string]interface{}, bool)
-	String() (string, error)
+	Update(source string, flags map[string]model.Flag, metadata model.Metadata)
 }

 var _ IStore = (*Store)(nil)
@@ -194,22 +191,6 @@ func (s *Store) Get(_ context.Context, key string, selector *Selector) (model.Fl
 	return flag, queryMeta, nil
 }

-func (f *Store) String() (string, error) {
-	f.logger.Debug("dumping flags to string")
-
-	state, _, err := f.GetAll(context.Background(), nil)
-	if err != nil {
-		return "", fmt.Errorf("unable to get all flags: %w", err)
-	}
-
-	bytes, err := json.Marshal(state)
-	if err != nil {
-		return "", fmt.Errorf("unable to marshal flags: %w", err)
-	}
-
-	return string(bytes), nil
-}
-
 // GetAll returns a copy of the store's state (copy in order to be concurrency safe)
 func (s *Store) GetAll(ctx context.Context, selector *Selector) (map[string]model.Flag, model.Metadata, error) {
 	flags := make(map[string]model.Flag)
@@ -229,9 +210,7 @@ func (s *Store) Update(
 	source string,
 	flags map[string]model.Flag,
 	metadata model.Metadata,
-) (map[string]interface{}, bool) {
-	resyncRequired := false
-
+) {
 	if source == "" {
 		panic("source cannot be empty")
 	}
@@ -313,7 +292,6 @@ func (s *Store) Update(
 	}

 	txn.Commit()
-	return notifications.NewFromFlags(oldFlags, flags), resyncRequired
 }

 // Watch the result-set of a selector for changes, sending updates to the watcher channel.
diff --git c/core/pkg/store/store_test.go i/core/pkg/store/store_test.go
index ced7e76..c6cf2dd 100644
--- c/core/pkg/store/store_test.go
+++ i/core/pkg/store/store_test.go
@@ -25,8 +25,6 @@ func TestUpdateFlags(t *testing.T) {
 		source      string
 		wantFlags   map[string]model.Flag
 		setMetadata model.Metadata
-		wantNotifs  map[string]interface{}
-		wantResync  bool
 	}{
 		{
 			name: "both nil",
@@ -37,10 +35,9 @@ func TestUpdateFlags(t *testing.T) {
 				}
 				return s
 			},
-			source:     source1,
-			newFlags:   nil,
-			wantFlags:  map[string]model.Flag{},
-			wantNotifs: map[string]interface{}{},
+			source:    source1,
+			newFlags:  nil,
+			wantFlags: map[string]model.Flag{},
 		},
 		{
 			name: "both empty flags",
@@ -51,10 +48,9 @@ func TestUpdateFlags(t *testing.T) {
 				}
 				return s
 			},
-			source:     source1,
-			newFlags:   map[string]model.Flag{},
-			wantFlags:  map[string]model.Flag{},
-			wantNotifs: map[string]interface{}{},
+			source:    source1,
+			newFlags:  map[string]model.Flag{},
+			wantFlags: map[string]model.Flag{},
 		},
 		{
 			name: "empty new",
@@ -65,10 +61,9 @@ func TestUpdateFlags(t *testing.T) {
 				}
 				return s
 			},
-			source:     source1,
-			newFlags:   nil,
-			wantFlags:  map[string]model.Flag{},
-			wantNotifs: map[string]interface{}{},
+			source:    source1,
+			newFlags:  nil,
+			wantFlags: map[string]model.Flag{},
 		},
 		{
 			name: "update from source 1 (old flag removed)",
@@ -89,10 +84,6 @@ func TestUpdateFlags(t *testing.T) {
 			wantFlags: map[string]model.Flag{
 				"paka": {Key: "paka", DefaultVariant: "on", Source: source1, FlagSetId: nilFlagSetId, Priority: 0},
 			},
-			wantNotifs: map[string]interface{}{
-				"paka": map[string]interface{}{"type": "write"},
-				"waka": map[string]interface{}{"type": "delete"},
-			},
 		},
 		{
 			name: "update from source 1 (new flag added)",
@@ -114,7 +105,6 @@ func TestUpdateFlags(t *testing.T) {
 				"waka": {Key: "waka", DefaultVariant: "off", Source: source1, FlagSetId: nilFlagSetId, Priority: 0},
 				"paka": {Key: "paka", DefaultVariant: "on", Source: source2, FlagSetId: nilFlagSetId, Priority: 1},
 			},
-			wantNotifs: map[string]interface{}{"paka": map[string]interface{}{"type": "write"}},
 		},
 		{
 			name: "flag set inheritance",
@@ -138,10 +128,6 @@ func TestUpdateFlags(t *testing.T) {
 				"waka": {Key: "waka", DefaultVariant: "on", Source: source1, FlagSetId: "topLevelSet", Priority: 0, Metadata: model.Metadata{"flagSetId": "topLevelSet"}},
 				"paka": {Key: "paka", DefaultVariant: "on", Source: source1, FlagSetId: "flagLevelSet", Priority: 0, Metadata: model.Metadata{"flagSetId": "flagLevelSet"}},
 			},
-			wantNotifs: map[string]interface{}{
-				"paka": map[string]interface{}{"type": "write"},
-				"waka": map[string]interface{}{"type": "write"},
-			},
 		},
 	}

@@ -150,12 +136,10 @@ func TestUpdateFlags(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			t.Parallel()
 			store := tt.setup(t)
-			gotNotifs, resyncRequired := store.Update(tt.source, tt.newFlags, tt.setMetadata)
+			store.Update(tt.source, tt.newFlags, tt.setMetadata)
 			gotFlags, _, _ := store.GetAll(context.Background(), nil)

 			require.Equal(t, tt.wantFlags, gotFlags)
-			require.Equal(t, tt.wantNotifs, gotNotifs)
-			require.Equal(t, tt.wantResync, resyncRequired)
 		})
 	}
 }
diff --git c/flagd/pkg/runtime/runtime.go i/flagd/pkg/runtime/runtime.go
index 511354b..df03f26 100644
--- c/flagd/pkg/runtime/runtime.go
+++ i/flagd/pkg/runtime/runtime.go
@@ -127,7 +127,7 @@ func (r *Runtime) updateAndEmit(payload sync.DataSync) {
 	r.mu.Lock()
 	defer r.mu.Unlock()

-	_, _, err := r.Evaluator.SetState(payload)
+	err := r.Evaluator.SetState(payload)
 	if err != nil {
 		r.Logger.Error(fmt.Sprintf("error setting state: %v", err))
 		return

diff --git c/core/pkg/evaluator/ievaluator.go i/core/pkg/evaluator/ievaluator.go
index 9f492c2..00b5590 100644
--- c/core/pkg/evaluator/ievaluator.go
+++ i/core/pkg/evaluator/ievaluator.go
@@ -34,8 +34,7 @@ func NewAnyValue(
 IEvaluator is an extension of IResolver, allowing storage updates and retrievals
 */
 type IEvaluator interface {
-	GetState() (string, error)
-	SetState(payload sync.DataSync) (map[string]interface{}, bool, error)
+	SetState(payload sync.DataSync) error
 	IResolver
 }

diff --git c/core/pkg/evaluator/json.go i/core/pkg/evaluator/json.go
index 432b4a7..12b862b 100644
--- c/core/pkg/evaluator/json.go
+++ i/core/pkg/evaluator/json.go
@@ -117,15 +117,7 @@ func NewJSON(logger *logger.Logger, s store.IStore, opts ...JSONEvaluatorOption)
 	return &ev
 }

-func (je *JSON) GetState() (string, error) {
-	s, err := je.store.String()
-	if err != nil {
-		return "", fmt.Errorf("unable to fetch evaluator state: %w", err)
-	}
-	return s, nil
-}
-
-func (je *JSON) SetState(payload sync.DataSync) (map[string]interface{}, bool, error) {
+func (je *JSON) SetState(payload sync.DataSync) error {
 	_, span := je.jsonEvalTracer.Start(
 		context.Background(),
 		"flagSync",
@@ -138,18 +130,12 @@ func (je *JSON) SetState(payload sync.DataSync) (map[string]interface{}, bool, e
 	if err != nil {
 		span.SetStatus(codes.Error, "flagSync error")
 		span.RecordError(err)
-		return nil, false, err
+		return err
 	}

-	var events map[string]interface{}
-	var reSync bool
+	je.store.Update(payload.Source, definition.Flags, definition.Metadata)

-	events, reSync = je.store.Update(payload.Source, definition.Flags, definition.Metadata)
-
-	// Number of events correlates to the number of flags changed through this sync, record it
-	span.SetAttributes(attribute.Int("feature_flag.change_count", len(events)))
-
-	return events, reSync, nil
+	return nil
 }

 // Resolver implementation for flagd flags. This resolver should be kept reusable, hence must interact with interfaces.
diff --git c/core/pkg/evaluator/json_test.go i/core/pkg/evaluator/json_test.go
index 733b0db..1e68def 100644
--- c/core/pkg/evaluator/json_test.go
+++ i/core/pkg/evaluator/json_test.go
@@ -6,7 +6,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"reflect"
-	"strings"
 	"testing"
 	"time"

@@ -383,29 +382,23 @@ var flagConfig = fmt.Sprintf(`{

 func TestGetState_Valid_ContainsFlag(t *testing.T) {
 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: ValidFlags, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: ValidFlags, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("Expected no error")
 	}

 	// get the state
-	state, err := evaluator.GetState()
-	if err != nil {
+	state := evaluator.ResolveAsAnyValue(context.Background(), "", "validFlag", nil)
+	if state.Error != nil {
 		t.Fatalf("expected no error")
 	}
-
-	// validate it contains the flag
-	wants := "validFlag"
-	if !strings.Contains(state, wants) {
-		t.Fatalf("expected: %s to contain: %s", state, wants)
-	}
 }

 func TestSetState_Invalid_Error(t *testing.T) {
 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

 	// set state with an invalid flag definition
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: InvalidFlags, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: InvalidFlags, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("unexpected error")
 	}
@@ -415,7 +408,7 @@ func TestSetState_Valid_NoError(t *testing.T) {
 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

 	// set state with a valid flag definition
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: ValidFlags, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: ValidFlags, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -423,7 +416,7 @@ func TestSetState_Valid_NoError(t *testing.T) {

 func TestResolveAllValues(t *testing.T) {
 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -492,7 +485,7 @@ func TestResolveBooleanValue(t *testing.T) {
 	}
 	const reqID = "default"
 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -527,7 +520,7 @@ func BenchmarkResolveBooleanValue(b *testing.B) {
 	}

 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		b.Fatalf("expected no error")
 	}
@@ -567,7 +560,7 @@ func TestResolveStringValue(t *testing.T) {
 	}
 	const reqID = "default"
 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -603,7 +596,7 @@ func BenchmarkResolveStringValue(b *testing.B) {
 	}

 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		b.Fatalf("expected no error")
 	}
@@ -643,7 +636,7 @@ func TestResolveFloatValue(t *testing.T) {
 	}
 	const reqID = "default"
 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -679,7 +672,7 @@ func BenchmarkResolveFloatValue(b *testing.B) {
 	}

 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		b.Fatalf("expected no error")
 	}
@@ -719,7 +712,7 @@ func TestResolveIntValue(t *testing.T) {
 	}
 	const reqID = "default"
 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -755,7 +748,7 @@ func BenchmarkResolveIntValue(b *testing.B) {
 	}

 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		b.Fatalf("expected no error")
 	}
@@ -795,7 +788,7 @@ func TestResolveObjectValue(t *testing.T) {
 	}
 	const reqID = "default"
 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -834,7 +827,7 @@ func BenchmarkResolveObjectValue(b *testing.B) {
 	}

 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		b.Fatalf("expected no error")
 	}
@@ -879,7 +872,7 @@ func TestResolveAsAnyValue(t *testing.T) {
 	}

 	evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-	_, _, err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+	err := evaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 	if err != nil {
 		t.Fatalf("expected no error")
 	}
@@ -915,7 +908,7 @@ func TestResolve_DefaultVariant(t *testing.T) {
 	for _, test := range tests {
 		t.Run("", func(t *testing.T) {
 			evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())
-			_, _, err := evaluator.SetState(sync.DataSync{FlagData: test.flags, Source: "testSource"})
+			err := evaluator.SetState(sync.DataSync{FlagData: test.flags, Source: "testSource"})

 			if err != nil {
 				t.Fatalf("expected no error")
@@ -982,7 +975,7 @@ func TestSetState_DefaultVariantValidation(t *testing.T) {
 		t.Run(name, func(t *testing.T) {
 			jsonEvaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-			_, _, err := jsonEvaluator.SetState(sync.DataSync{FlagData: tt.jsonFlags, Source: "testSource"})
+			err := jsonEvaluator.SetState(sync.DataSync{FlagData: tt.jsonFlags, Source: "testSource"})

 			if tt.valid && err != nil {
 				t.Error(err)
@@ -994,7 +987,7 @@ func TestSetState_DefaultVariantValidation(t *testing.T) {
 func TestState_Evaluator(t *testing.T) {
 	tests := map[string]struct {
 		inputState          string
-		expectedOutputState string
+		expectedOutputState map[string]model.Flag
 		expectedError       bool
 		expectedResync      bool
 	}{
@@ -1011,6 +1004,9 @@ func TestState_Evaluator(t *testing.T) {
 						  },
 						  "defaultVariant": "recursive",
 						  "state": "ENABLED",
+						  "metadata": {
+						    "flagSetId": "flagSetId"
+						  },
 						  "targeting": {
 							"if": [
 							  {
@@ -1029,20 +1025,19 @@ func TestState_Evaluator(t *testing.T) {
   					}
 				}
 			`,
-			expectedOutputState: `
-				{
-  					"flags": {
-						"fibAlgo": {
-						  "variants": {
-							"recursive": "recursive",
-							"memo": "memo",
-							"loop": "loop",
-							"binet": "binet"
-						  },
-						  "defaultVariant": "recursive",
-						  "state": "ENABLED",
-						  "source":"testSource",
-						  "targeting": {
+			expectedOutputState: map[string]model.Flag{
+				"fibAlgo": {
+					Key: "fibAlgo",
+					Variants: map[string]any{
+						"recursive": "recursive",
+						"memo":      "memo",
+						"loop":      "loop",
+						"binet":     "binet",
+					},
+					DefaultVariant: "recursive",
+					State:          "ENABLED",
+					Source:         "testSource",
+					Targeting: json.RawMessage(`{
 							"if": [
 							  {
 								"in": ["@faas.com", {
@@ -1050,12 +1045,13 @@ func TestState_Evaluator(t *testing.T) {
 							  }]
 							  }, "binet", null
 							]
-						  }
-    					}
+						  }`),
+					Metadata: map[string]interface{}{
+						"flagSetId": "flagSetId",
 					},
-					"flagSources":null
-				}
-			`,
+					FlagSetId: "flagSetId",
+				},
+			},
 		},
 		"no-indentation": {
 			inputState: `
@@ -1070,6 +1066,9 @@ func TestState_Evaluator(t *testing.T) {
 				},
 				"defaultVariant": "recursive",
 				"state": "ENABLED",
+				"metadata": {
+				  "flagSetId": "flagSetId"
+				},
 				"targeting": {
 				"if": [
 				{
@@ -1088,20 +1087,19 @@ func TestState_Evaluator(t *testing.T) {
 				}
 				}
 			`,
-			expectedOutputState: `
-				{
-  					"flags": {
-						"fibAlgo": {
-						  "variants": {
-							"recursive": "recursive",
-							"memo": "memo",
-							"loop": "loop",
-							"binet": "binet"
-						  },
-						  "defaultVariant": "recursive",
-						  "state": "ENABLED",
-						  "source":"testSource",
-						  "targeting": {
+			expectedOutputState: map[string]model.Flag{
+				"fibAlgo": {
+					Key: "fibAlgo",
+					Variants: map[string]any{
+						"recursive": "recursive",
+						"memo":      "memo",
+						"loop":      "loop",
+						"binet":     "binet",
+					},
+					DefaultVariant: "recursive",
+					State:          "ENABLED",
+					Source:         "testSource",
+					Targeting: json.RawMessage(`{
 							"if": [
 							  {
 								"in": ["@faas.com", {
@@ -1109,12 +1107,13 @@ func TestState_Evaluator(t *testing.T) {
 							  }]
 							  }, "binet", null
 							]
-						  }
-    					}
+						  }`),
+					Metadata: map[string]interface{}{
+						"flagSetId": "flagSetId",
 					},
-					"flagSources":null
-				}
-			`,
+					FlagSetId: "flagSetId",
+				},
+			},
 		},
 		"invalid evaluator json": {
 			inputState: `
@@ -1129,6 +1128,9 @@ func TestState_Evaluator(t *testing.T) {
 						  },
 						  "defaultVariant": "recursive",
 						  "state": "ENABLED",
+						  "metadata": {
+						    "flagSetId": "flagSetId"
+						  },
 						  "targeting": {
 							"if": [
 							  {
@@ -1158,6 +1160,9 @@ func TestState_Evaluator(t *testing.T) {
 						},
 						"defaultVariant": "recursive",
 						"state": "ENABLED",
+						"metadata": {
+						  "flagSetId": "flagSetId"
+						},
 						"targeting": {
 						"if": [
 							{
@@ -1176,6 +1181,9 @@ func TestState_Evaluator(t *testing.T) {
 						},
 						"defaultVariant": "off",
 						"source":"testSource",
+						"metadata": {
+						  "flagSetId": "flagSetId"
+						},
 						"targeting": {
 							"if": [
 								{
@@ -1197,57 +1205,55 @@ func TestState_Evaluator(t *testing.T) {
 			}
 		`,
 			expectedError: false,
-			expectedOutputState: `
-			{
-					"flags": {
-					"fibAlgo": {
-						"variants": {
+			expectedOutputState: map[string]model.Flag{
+				"fibAlgo": {
+					Key: "fibAlgo",
+					Variants: map[string]any{
 						"recursive": "recursive",
-						"memo": "memo",
-						"loop": "loop",
-						"binet": "binet"
-						},
-						"defaultVariant": "recursive",
-						"state": "ENABLED",
-						"source":"testSource",
-						"targeting": {
-						"if": [
-							{
-							"in": ["@faas.com", {
-							"var": ["email"]
-							}]
-							}, "binet", "null", "loop"
-						]
-						}
-						},
-					"isColorYellow": {
-						"state": "ENABLED",
-						"variants": {
-							"on": true,
-							"off": false
-						},
-						"defaultVariant": "off",
-						"source":"testSource",
-						"targeting": {
+						"memo":      "memo",
+						"loop":      "loop",
+						"binet":     "binet",
+					},
+					DefaultVariant: "recursive",
+					State:          "ENABLED",
+					Source:         "testSource",
+					Targeting: json.RawMessage(`{
+							"if": [
+							  {
+								"in": ["@faas.com", {
+								"var": ["email"]
+							  }]
+							  }, "binet", null
+							]
+						  }`),
+					Metadata: map[string]interface{}{
+						"flagSetId": "flagSetId",
+					},
+					FlagSetId: "flagSetId",
+				},
+				"isColorYellow": {
+					Key: "isColorYellow",
+					Variants: map[string]any{
+						"on":  true,
+						"off": false,
+					},
+					DefaultVariant: "off",
+					State:          "ENABLED",
+					Source:         "testSource",
+					Targeting: json.RawMessage(`{
 							"if": [
 								{
 									"==": [
-										{
-											"varr": ["color"]
-										},
-										"yellow"
-									]
-								},
-								"on",
 								"off",
 								"none"
 							]
-						}
-					}
+						}`),
+					Metadata: map[string]interface{}{
+						"flagSetId": "flagSetId",
+					},
+					FlagSetId: "flagSetId",
 				},
-				"flagSources":null
-			}
-		`,
+			},
 		},
 		"empty evaluator": {
 			inputState: `
@@ -1284,46 +1290,55 @@ func TestState_Evaluator(t *testing.T) {
 		t.Run(name, func(t *testing.T) {
 			jsonEvaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-			_, resync, err := jsonEvaluator.SetState(sync.DataSync{FlagData: tt.inputState, Source: "testSource"})
+			err := jsonEvaluator.SetState(sync.DataSync{FlagData: tt.inputState, Source: "testSource"})
 			if err != nil {
 				if !tt.expectedError {
 					t.Error(err)
 				}
-				if resync != tt.expectedResync {
-					t.Errorf("expected resync %t got %t", tt.expectedResync, resync)
-				}
 				return
 			} else if tt.expectedError {
 				t.Error("expected error, got nil")
 				return
 			}

-			if resync != tt.expectedResync {
-				t.Errorf("expected resync %t got %t", tt.expectedResync, resync)
-			}
-
-			got, err := jsonEvaluator.GetState()
+			got, _, err := jsonEvaluator.store.GetAll(context.Background(), nil)
 			if err != nil {
 				t.Error(err)
 			}

-			var expectedOutputJSON map[string]interface{}
-			if err := json.Unmarshal([]byte(tt.expectedOutputState), &expectedOutputJSON); err != nil {
-				t.Fatal(err)
-			}
+			for _, v := range got {

-			var gotOutputJSON map[string]interface{}
-			if err := json.Unmarshal([]byte(got), &gotOutputJSON); err != nil {
-				t.Fatal(err)
-			}
+				// json data can be formatted differently, hence we remove it from the object and compare separately
+				parsedTargeting, _ := normalizeJSON(v.Targeting)
+				flag := tt.expectedOutputState[v.Key]
+				flag.Targeting = nil
+				gotTargeting, _ := normalizeJSON(v.Targeting)
+				v.Targeting = nil

-			if !reflect.DeepEqual(expectedOutputJSON["flags"], gotOutputJSON) {
-				t.Errorf("expected state: %v got state: %v", expectedOutputJSON["flags"], gotOutputJSON)
+				if !reflect.DeepEqual(parsedTargeting, gotTargeting) {
+					t.Errorf("\nexpected targeting: %s\ngot targeting: %s", parsedTargeting, gotTargeting)
+				}
+
+				if !reflect.DeepEqual(flag, v) {
+					t.Errorf("expected state: %v got state: %v", flag, v)
+				}
 			}
 		})
 	}
 }

+func normalizeJSON(jsonData []byte) (interface{}, error) {
+	var result interface{}
+	if jsonData == nil {
+		return nil, nil // Handle nil gracefully
+	}
+	err := json.Unmarshal(jsonData, &result)
+	if err != nil {
+		return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
+	}
+	return result, nil
+}
+
 func TestFlagStateSafeForConcurrentReadWrites(t *testing.T) {
 	tests := map[string]struct {
 		flagResolution func(evaluator *evaluator.JSON) error
@@ -1391,7 +1406,7 @@ func TestFlagStateSafeForConcurrentReadWrites(t *testing.T) {
 		t.Run(name, func(t *testing.T) {
 			jsonEvaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-			_, _, err := jsonEvaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+			err := jsonEvaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -1414,7 +1429,7 @@ func TestFlagStateSafeForConcurrentReadWrites(t *testing.T) {
 						errChan <- nil
 						return
 					default:
-						_, _, err := jsonEvaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
+						err := jsonEvaluator.SetState(sync.DataSync{FlagData: flagConfig, Source: "testSource"})
 						if err != nil {
 							errChan <- err
 							return
@@ -1456,7 +1471,7 @@ func TestFlagdAmbientProperties(t *testing.T) {
 	t.Run("flagKeyIsInTheContext", func(t *testing.T) {
 		evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-		_, _, err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
+		err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
 			"flags": {
 				"welcome-banner": {
 					"state": "ENABLED",
@@ -1496,7 +1511,7 @@ func TestFlagdAmbientProperties(t *testing.T) {
 	t.Run("timestampIsInTheContext", func(t *testing.T) {
 		evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-		_, _, err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
+		err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
 			"flags": {
 				"welcome-banner": {
 					"state": "ENABLED",
@@ -1530,7 +1545,7 @@ func TestTargetingVariantBehavior(t *testing.T) {
 	t.Run("missing variant error", func(t *testing.T) {
 		evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-		_, _, err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
+		err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
 			"flags": {
 				"missing-variant": {
 					"state": "ENABLED",
@@ -1558,7 +1573,7 @@ func TestTargetingVariantBehavior(t *testing.T) {
 	t.Run("null fallback", func(t *testing.T) {
 		evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

-		_, _, err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
+		err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
 			"flags": {
 				"null-fallback": {
 					"state": "ENABLED",
@@ -1591,7 +1606,7 @@ func TestTargetingVariantBehavior(t *testing.T) {
 		evaluator := evaluator.NewJSON(logger.NewLogger(nil, false), store.NewFlags())

 		//nolint:dupword
-		_, _, err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
+		err := evaluator.SetState(sync.DataSync{Source: "testSource", FlagData: `{
 			"flags": {
 				"match-boolean": {
 					"state": "ENABLED",
diff --git c/core/pkg/evaluator/mock/ievaluator.go i/core/pkg/evaluator/mock/ievaluator.go
index 98d0367..bf0ce56 100644
--- c/core/pkg/evaluator/mock/ievaluator.go
+++ i/core/pkg/evaluator/mock/ievaluator.go
@@ -42,21 +42,6 @@ func (m *MockIEvaluator) EXPECT() *MockIEvaluatorMockRecorder {
 	return m.recorder
 }

-// GetState mocks base method.
-func (m *MockIEvaluator) GetState() (string, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetState")
-	ret0, _ := ret[0].(string)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// GetState indicates an expected call of GetState.
-func (mr *MockIEvaluatorMockRecorder) GetState() *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetState", reflect.TypeOf((*MockIEvaluator)(nil).GetState))
-}
-
 // ResolveAllValues mocks base method.
 func (m *MockIEvaluator) ResolveAllValues(ctx context.Context, reqID string, context map[string]any) ([]evaluator.AnyValue, model.Metadata, error) {
 	m.ctrl.T.Helper()
@@ -178,13 +163,11 @@ func (mr *MockIEvaluatorMockRecorder) ResolveStringValue(ctx, reqID, flagKey, co
 }

 // SetState mocks base method.
-func (m *MockIEvaluator) SetState(payload sync.DataSync) (map[string]any, bool, error) {
+func (m *MockIEvaluator) SetState(payload sync.DataSync) error {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "SetState", payload)
-	ret0, _ := ret[0].(map[string]any)
-	ret1, _ := ret[1].(bool)
-	ret2, _ := ret[2].(error)
-	return ret0, ret1, ret2
+	ret0, _ := ret[0].(error)
+	return ret0
 }

 // SetState indicates an expected call of SetState.
diff --git c/core/pkg/store/store.go i/core/pkg/store/store.go
index 2b8b87c..0c6bc16 100644
--- c/core/pkg/store/store.go
+++ i/core/pkg/store/store.go
@@ -2,14 +2,12 @@ package store

 import (
 	"context"
-	"encoding/json"
 	"fmt"
 	"slices"

 	"github.com/hashicorp/go-memdb"
 	"github.com/open-feature/flagd/core/pkg/logger"
 	"github.com/open-feature/flagd/core/pkg/model"
-	"github.com/open-feature/flagd/core/pkg/notifications"
 )

 var noValidatedSources = []string{}
@@ -24,8 +22,7 @@ type IStore interface {
 	Get(ctx context.Context, key string, selector *Selector) (model.Flag, model.Metadata, error)
 	GetAll(ctx context.Context, selector *Selector) (map[string]model.Flag, model.Metadata, error)
 	Watch(ctx context.Context, selector *Selector, watcher chan<- FlagQueryResult)
-	Update(source string, flags map[string]model.Flag, metadata model.Metadata) (map[string]interface{}, bool)
-	String() (string, error)
+	Update(source string, flags map[string]model.Flag, metadata model.Metadata)
 }

 var _ IStore = (*Store)(nil)
@@ -194,22 +191,6 @@ func (s *Store) Get(_ context.Context, key string, selector *Selector) (model.Fl
 	return flag, queryMeta, nil
 }

-func (f *Store) String() (string, error) {
-	f.logger.Debug("dumping flags to string")
-
-	state, _, err := f.GetAll(context.Background(), nil)
-	if err != nil {
-		return "", fmt.Errorf("unable to get all flags: %w", err)
-	}
-
-	bytes, err := json.Marshal(state)
-	if err != nil {
-		return "", fmt.Errorf("unable to marshal flags: %w", err)
-	}
-
-	return string(bytes), nil
-}
-
 // GetAll returns a copy of the store's state (copy in order to be concurrency safe)
 func (s *Store) GetAll(ctx context.Context, selector *Selector) (map[string]model.Flag, model.Metadata, error) {
 	flags := make(map[string]model.Flag)
@@ -229,9 +210,7 @@ func (s *Store) Update(
 	source string,
 	flags map[string]model.Flag,
 	metadata model.Metadata,
-) (map[string]interface{}, bool) {
-	resyncRequired := false
-
+) {
 	if source == "" {
 		panic("source cannot be empty")
 	}
@@ -313,7 +292,6 @@ func (s *Store) Update(
 	}

 	txn.Commit()
-	return notifications.NewFromFlags(oldFlags, flags), resyncRequired
 }

 // Watch the result-set of a selector for changes, sending updates to the watcher channel.
diff --git c/core/pkg/store/store_test.go i/core/pkg/store/store_test.go
index ced7e76..c6cf2dd 100644
--- c/core/pkg/store/store_test.go
+++ i/core/pkg/store/store_test.go
@@ -25,8 +25,6 @@ func TestUpdateFlags(t *testing.T) {
 		source      string
 		wantFlags   map[string]model.Flag
 		setMetadata model.Metadata
-		wantNotifs  map[string]interface{}
-		wantResync  bool
 	}{
 		{
 			name: "both nil",
@@ -37,10 +35,9 @@ func TestUpdateFlags(t *testing.T) {
 				}
 				return s
 			},
-			source:     source1,
-			newFlags:   nil,
-			wantFlags:  map[string]model.Flag{},
-			wantNotifs: map[string]interface{}{},
+			source:    source1,
+			newFlags:  nil,
+			wantFlags: map[string]model.Flag{},
 		},
 		{
 			name: "both empty flags",
@@ -51,10 +48,9 @@ func TestUpdateFlags(t *testing.T) {
 				}
 				return s
 			},
-			source:     source1,
-			newFlags:   map[string]model.Flag{},
-			wantFlags:  map[string]model.Flag{},
-			wantNotifs: map[string]interface{}{},
+			source:    source1,
+			newFlags:  map[string]model.Flag{},
+			wantFlags: map[string]model.Flag{},
 		},
 		{
 			name: "empty new",
@@ -65,10 +61,9 @@ func TestUpdateFlags(t *testing.T) {
 				}
 				return s
 			},
-			source:     source1,
-			newFlags:   nil,
-			wantFlags:  map[string]model.Flag{},
-			wantNotifs: map[string]interface{}{},
+			source:    source1,
+			newFlags:  nil,
+			wantFlags: map[string]model.Flag{},
 		},
 		{
 			name: "update from source 1 (old flag removed)",
@@ -89,10 +84,6 @@ func TestUpdateFlags(t *testing.T) {
 			wantFlags: map[string]model.Flag{
 				"paka": {Key: "paka", DefaultVariant: "on", Source: source1, FlagSetId: nilFlagSetId, Priority: 0},
 			},
-			wantNotifs: map[string]interface{}{
-				"paka": map[string]interface{}{"type": "write"},
-				"waka": map[string]interface{}{"type": "delete"},
-			},
 		},
 		{
 			name: "update from source 1 (new flag added)",
@@ -114,7 +105,6 @@ func TestUpdateFlags(t *testing.T) {
 				"waka": {Key: "waka", DefaultVariant: "off", Source: source1, FlagSetId: nilFlagSetId, Priority: 0},
 				"paka": {Key: "paka", DefaultVariant: "on", Source: source2, FlagSetId: nilFlagSetId, Priority: 1},
 			},
-			wantNotifs: map[string]interface{}{"paka": map[string]interface{}{"type": "write"}},
 		},
 		{
 			name: "flag set inheritance",
@@ -138,10 +128,6 @@ func TestUpdateFlags(t *testing.T) {
 				"waka": {Key: "waka", DefaultVariant: "on", Source: source1, FlagSetId: "topLevelSet", Priority: 0, Metadata: model.Metadata{"flagSetId": "topLevelSet"}},
 				"paka": {Key: "paka", DefaultVariant: "on", Source: source1, FlagSetId: "flagLevelSet", Priority: 0, Metadata: model.Metadata{"flagSetId": "flagLevelSet"}},
 			},
-			wantNotifs: map[string]interface{}{
-				"paka": map[string]interface{}{"type": "write"},
-				"waka": map[string]interface{}{"type": "write"},
-			},
 		},
 	}

@@ -150,12 +136,10 @@ func TestUpdateFlags(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			t.Parallel()
 			store := tt.setup(t)
-			gotNotifs, resyncRequired := store.Update(tt.source, tt.newFlags, tt.setMetadata)
+			store.Update(tt.source, tt.newFlags, tt.setMetadata)
 			gotFlags, _, _ := store.GetAll(context.Background(), nil)

 			require.Equal(t, tt.wantFlags, gotFlags)
-			require.Equal(t, tt.wantNotifs, gotNotifs)
-			require.Equal(t, tt.wantResync, resyncRequired)
 		})
 	}
 }
diff --git c/flagd/pkg/runtime/runtime.go i/flagd/pkg/runtime/runtime.go
index 511354b..df03f26 100644
--- c/flagd/pkg/runtime/runtime.go
+++ i/flagd/pkg/runtime/runtime.go
@@ -127,7 +127,7 @@ func (r *Runtime) updateAndEmit(payload sync.DataSync) {
 	r.mu.Lock()
 	defer r.mu.Unlock()

-	_, _, err := r.Evaluator.SetState(payload)
+	err := r.Evaluator.SetState(payload)
 	if err != nil {
 		r.Logger.Error(fmt.Sprintf("error setting state: %v", err))
 		return
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
@aepfli aepfli force-pushed the refactor/cleanup_ievaluator_interface branch from a4b9efc to 41c8b62 Compare September 8, 2025 14:27
@toddbaert toddbaert merged commit aa504f7 into open-feature:main Sep 8, 2025
15 checks passed
@github-actions github-actions Bot mentioned this pull request Sep 8, 2025
thisthat pushed a commit to thisthat/flagd that referenced this pull request Sep 29, 2025
depends: open-feature#1792 for fixing build issues

---------

Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
Signed-off-by: Giovanni Liva <giovanni.liva@dynatrace.com>
toddbaert added a commit that referenced this pull request Dec 24, 2025
🤖 I have created a release *beep* *boop*
---


<details><summary>flagd: 0.13.0</summary>

##
[0.13.0](flagd/v0.12.9...flagd/v0.13.0)
(2025-12-23)


### 🐛 Bug Fixes

* fixing sync return format missing flag layer, adding full e2e suite
([#1827](#1827))
([570693d](570693d))
* **security:** update module github.com/go-viper/mapstructure/v2 to
v2.4.0 [security]
([#1784](#1784))
([037e30b](037e30b))
* **security:** update module golang.org/x/crypto to v0.45.0 [security]
([#1826](#1826))
([7e0762b](7e0762b))


### ✨ New Features

* add support for http-based ofrep metrics
([#1803](#1803))
([fcd19b3](fcd19b3))
* cleanup evaluator interface
([#1793](#1793))
([aa504f7](aa504f7))
* enable parsing of array flag configurations for flagd
([#1797](#1797))
([97c6ffa](97c6ffa))
* multi-project support via selectors and flagSetId namespacing
([#1702](#1702))
([f9ce46f](f9ce46f))
* normalize selector in sync (use header as in OFREP and RPC)
([#1815](#1815))
([c1f06cb](c1f06cb))


### 🧹 Chore

* **refactor:** use memdb for flag storage
([#1697](#1697))
([5c5c1cf](5c5c1cf))


### 🔄 Refactoring

* store cleanup
([#1705](#1705))
([bcff8d7](bcff8d7))
</details>

<details><summary>flagd-proxy: 0.8.1</summary>

##
[0.8.1](flagd-proxy/v0.8.0...flagd-proxy/v0.8.1)
(2025-12-23)


### 🐛 Bug Fixes

* **security:** update module github.com/go-viper/mapstructure/v2 to
v2.4.0 [security]
([#1784](#1784))
([037e30b](037e30b))
* **security:** update module golang.org/x/crypto to v0.45.0 [security]
([#1826](#1826))
([7e0762b](7e0762b))
</details>

<details><summary>core: 0.13.0</summary>

##
[0.13.0](core/v0.12.1...core/v0.13.0)
(2025-12-23)


### ⚠ BREAKING CHANGES

* enable parsing of array flag configurations for flagd
([#1797](#1797))
* cleanup evaluator interface
([#1793](#1793))
* removes the `fractionalEvaluation` operator since it has been replaced
with `fractional`.
([#1704](#1704))

### 🐛 Bug Fixes

* **security:** update module github.com/go-viper/mapstructure/v2 to
v2.4.0 [security]
([#1784](#1784))
([037e30b](037e30b))
* **security:** update module golang.org/x/crypto to v0.45.0 [security]
([#1825](#1825))
([44edcc9](44edcc9))
* **security:** update module golang.org/x/crypto to v0.45.0 [security]
([#1826](#1826))
([7e0762b](7e0762b))


### ✨ New Features

* Add OAuth support for HTTP Sync
([#1791](#1791))
([268fd75](268fd75))
* Add OTEL default variables
([#1812](#1812))
([c2e3fc6](c2e3fc6))
* allow null flagSetId Selector, restrict Selector to single
key-value-pairs
([#1708](#1708))
([#1811](#1811))
([c12a0ae](c12a0ae))
* change jsonschema parser
([#1794](#1794))
([bf3f722](bf3f722))
* cleanup evaluator interface
([#1793](#1793))
([aa504f7](aa504f7))
* enable parsing of array flag configurations for flagd
([#1797](#1797))
([97c6ffa](97c6ffa))
* multi-project support via selectors and flagSetId namespacing
([#1702](#1702))
([f9ce46f](f9ce46f))


### 🧹 Chore

* **refactor:** use memdb for flag storage
([#1697](#1697))
([5c5c1cf](5c5c1cf))
* removes the `fractionalEvaluation` operator since it has been replaced
with `fractional`.
([#1704](#1704))
([3228ad8](3228ad8))


### 🔄 Refactoring

* remove deprecated bearerToken option
([#1816](#1816))
([efda06a](efda06a))
* removed unused Selector from Flag and Store.
([#1747](#1747))
([1083005](1083005))
* store cleanup
([#1705](#1705))
([bcff8d7](bcff8d7))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Signed-off-by: OpenFeature Bot <109696520+openfeaturebot@users.noreply.github.com>
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants