+ * Implementations must define a no-args constructor.
+ *
+ * @author Chris Bono
+ * @since 5.0
+ */
+public interface CassandraRepositoryFragmentsContributor extends RepositoryFragmentsContributor {
+
+	CassandraRepositoryFragmentsContributor DEFAULT = EmptyFragmentsContributor.INSTANCE;
+
+	/**
+	 * Returns a composed {@code CassandraRepositoryFragmentsContributor} that first applies this contributor to its inputs,
+	 * and then applies the {@code after} contributor concatenating effectively both results. If evaluation of either
+	 * contributors throws an exception, it is relayed to the caller of the composed contributor.
+	 *
+	 * @param after the contributor to apply after this contributor is applied.
+	 * @return a composed contributor that first applies this contributor and then applies the {@code after} contributor.
+	 */
+	default CassandraRepositoryFragmentsContributor andThen(CassandraRepositoryFragmentsContributor after) {
+
+		Assert.notNull(after, "CassandraRepositoryFragmentsContributor must not be null");
+
+		return new CassandraRepositoryFragmentsContributor() {
+
+			@Override
+			public RepositoryFragments contribute(RepositoryMetadata metadata,
+					CassandraEntityInformation, ?> entityInformation, CassandraOperations operations) {
+				return CassandraRepositoryFragmentsContributor.this.contribute(metadata, entityInformation, operations)
+						.append(after.contribute(metadata, entityInformation, operations));
+			}
+
+			@Override
+			public RepositoryFragments describe(RepositoryMetadata metadata) {
+				return CassandraRepositoryFragmentsContributor.this.describe(metadata).append(after.describe(metadata));
+			}
+		};
+	}
+
+	/**
+	 * Creates {@link RepositoryFragments} based on {@link RepositoryMetadata} to add
+	 * Cassandra-specific extensions.
+	 *
+	 * @param metadata repository metadata.
+	 * @param entityInformation must not be {@literal null}.
+	 * @param operations must not be {@literal null}.
+	 * @return {@link RepositoryFragments} to be added to the repository.
+	 */
+	RepositoryFragments contribute(RepositoryMetadata metadata,
+			CassandraEntityInformation, ?> entityInformation, CassandraOperations operations);
+
+}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/EmptyFragmentsContributor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/EmptyFragmentsContributor.java
new file mode 100644
index 000000000..3bf69f572
--- /dev/null
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/EmptyFragmentsContributor.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.cassandra.repository.support;
+
+import org.springframework.data.cassandra.core.CassandraOperations;
+import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
+import org.springframework.data.repository.core.RepositoryMetadata;
+import org.springframework.data.repository.core.support.RepositoryComposition;
+
+/**
+ * Implementation of {@link CassandraRepositoryFragmentsContributor} that contributes empty fragments by default.
+ *
+ * @author Chris Bono
+ * @since 5.0
+ */
+enum EmptyFragmentsContributor implements CassandraRepositoryFragmentsContributor {
+
+	INSTANCE;
+
+	@Override
+	public RepositoryComposition.RepositoryFragments contribute(RepositoryMetadata metadata,
+			CassandraEntityInformation, ?> entityInformation, CassandraOperations operations) {
+		return RepositoryComposition.RepositoryFragments.empty();
+	}
+
+	@Override
+	public RepositoryComposition.RepositoryFragments describe(RepositoryMetadata metadata) {
+		return RepositoryComposition.RepositoryFragments.empty();
+	}
+}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveCassandraRepositoryFactory.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveCassandraRepositoryFactory.java
index 65dc0f6d1..fea55f9d6 100644
--- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveCassandraRepositoryFactory.java
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveCassandraRepositoryFactory.java
@@ -34,6 +34,7 @@
 import org.springframework.data.repository.core.RepositoryInformation;
 import org.springframework.data.repository.core.RepositoryMetadata;
 import org.springframework.data.repository.core.support.ReactiveRepositoryFactorySupport;
+import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
 import org.springframework.data.repository.query.CachingValueExpressionDelegate;
 import org.springframework.data.repository.query.QueryLookupStrategy;
 import org.springframework.data.repository.query.QueryLookupStrategy.Key;
@@ -46,6 +47,7 @@
  *
  * @author Mark Paluch
  * @author Marcin Grzejszczak
+ * @author Chris Bono
  * @since 2.0
  */
 public class ReactiveCassandraRepositoryFactory extends ReactiveRepositoryFactorySupport {
@@ -54,6 +56,8 @@ public class ReactiveCassandraRepositoryFactory extends ReactiveRepositoryFactor
 
 	private final MappingContext extends CassandraPersistentEntity>, ? extends CassandraPersistentProperty> mappingContext;
 
+	private ReactiveCassandraRepositoryFragmentsContributor fragmentsContributor = ReactiveCassandraRepositoryFragmentsContributor.DEFAULT;
+
 	/**
 	 * Create a new {@link ReactiveCassandraRepositoryFactory} with the given {@link ReactiveCassandraOperations}.
 	 *
@@ -67,6 +71,17 @@ public ReactiveCassandraRepositoryFactory(ReactiveCassandraOperations cassandraO
 		this.mappingContext = cassandraOperations.getConverter().getMappingContext();
 	}
 
+	/**
+	 * Configures the {@link ReactiveCassandraRepositoryFragmentsContributor} to be used. Defaults to
+	 * {@link ReactiveCassandraRepositoryFragmentsContributor#DEFAULT}.
+	 *
+	 * @param fragmentsContributor
+	 * @since 5.0
+	 */
+	public void setFragmentsContributor(ReactiveCassandraRepositoryFragmentsContributor fragmentsContributor) {
+		this.fragmentsContributor = fragmentsContributor;
+	}
+
 	@Override
 	protected ProjectionFactory getProjectionFactory(@Nullable ClassLoader classLoader,
 			@Nullable BeanFactory beanFactory) {
@@ -101,6 +116,24 @@ public  
+ * Implementations must define a no-args constructor.
+ *
+ * @author Chris Bono
+ * @since 5.0
+ */
+public interface ReactiveCassandraRepositoryFragmentsContributor extends RepositoryFragmentsContributor {
+
+	ReactiveCassandraRepositoryFragmentsContributor DEFAULT = ReactiveEmptyFragmentsContributor.INSTANCE;
+
+	/**
+	 * Returns a composed {@code ReactiveCassandraRepositoryFragmentsContributor} that first applies this contributor to
+	 * its inputs, and then applies the {@code after} contributor concatenating effectively both results. If evaluation
+	 * of either contributors throws an exception, it is relayed to the caller of the composed contributor.
+	 *
+	 * @param after the contributor to apply after this contributor is applied.
+	 * @return a composed contributor that first applies this contributor and then applies the {@code after} contributor.
+	 */
+	default ReactiveCassandraRepositoryFragmentsContributor andThen(ReactiveCassandraRepositoryFragmentsContributor after) {
+
+		Assert.notNull(after, "ReactiveCassandraRepositoryFragmentsContributor must not be null");
+
+		return new ReactiveCassandraRepositoryFragmentsContributor() {
+
+			@Override
+			public RepositoryFragments contribute(RepositoryMetadata metadata,
+					CassandraEntityInformation, ?> entityInformation, ReactiveCassandraOperations operations) {
+				return ReactiveCassandraRepositoryFragmentsContributor.this.contribute(metadata, entityInformation, operations)
+						.append(after.contribute(metadata, entityInformation, operations));
+			}
+
+			@Override
+			public RepositoryFragments describe(RepositoryMetadata metadata) {
+				return ReactiveCassandraRepositoryFragmentsContributor.this.describe(metadata).append(after.describe(metadata));
+			}
+		};
+	}
+
+	/**
+	 * Creates {@link RepositoryFragments} based on {@link RepositoryMetadata} to add
+	 * Cassandra-specific extensions.
+	 *
+	 * @param metadata repository metadata.
+	 * @param entityInformation must not be {@literal null}.
+	 * @param operations must not be {@literal null}.
+	 * @return {@link RepositoryFragments} to be added to the repository.
+	 */
+	RepositoryFragments contribute(RepositoryMetadata metadata,
+			CassandraEntityInformation, ?> entityInformation, ReactiveCassandraOperations operations);
+
+}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveEmptyFragmentsContributor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveEmptyFragmentsContributor.java
new file mode 100644
index 000000000..76426b560
--- /dev/null
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/ReactiveEmptyFragmentsContributor.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.cassandra.repository.support;
+
+import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
+import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
+import org.springframework.data.repository.core.RepositoryMetadata;
+import org.springframework.data.repository.core.support.RepositoryComposition;
+
+/**
+ * Implementation of {@link ReactiveCassandraRepositoryFragmentsContributor} that contributes empty fragments by
+ * default.
+ *
+ * @author Chris Bono
+ * @since 5.0
+ */
+enum ReactiveEmptyFragmentsContributor implements ReactiveCassandraRepositoryFragmentsContributor {
+
+	INSTANCE;
+
+	@Override
+	public RepositoryComposition.RepositoryFragments contribute(RepositoryMetadata metadata,
+			CassandraEntityInformation, ?> entityInformation, ReactiveCassandraOperations operations) {
+		return RepositoryComposition.RepositoryFragments.empty();
+	}
+
+	@Override
+	public RepositoryComposition.RepositoryFragments describe(RepositoryMetadata metadata) {
+		return RepositoryComposition.RepositoryFragments.empty();
+	}
+}
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/support/CassandraRepositoryFragmentsContributorUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/support/CassandraRepositoryFragmentsContributorUnitTests.java
new file mode 100644
index 000000000..2a457d37e
--- /dev/null
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/support/CassandraRepositoryFragmentsContributorUnitTests.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.cassandra.support;
+
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.cassandra.core.CassandraOperations;
+import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
+import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
+import org.springframework.data.cassandra.domain.User;
+import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
+import org.springframework.data.cassandra.repository.support.CassandraRepositoryFragmentsContributor;
+import org.springframework.data.cassandra.repository.support.MappingCassandraEntityInformation;
+import org.springframework.data.repository.Repository;
+import org.springframework.data.repository.core.RepositoryMetadata;
+import org.springframework.data.repository.core.support.AbstractRepositoryMetadata;
+import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
+import org.springframework.data.repository.core.support.RepositoryFragment;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * Unit tests for {@link CassandraRepositoryFragmentsContributor}.
+ *
+ * @author Chris Bono
+ */
+class CassandraRepositoryFragmentsContributorUnitTests {
+
+	@Test // GH-3279
+	void composedContributorShouldCreateFragments() {
+
+		var mappingContext = new CassandraMappingContext();
+		var converter = new MappingCassandraConverter(mappingContext);
+		CassandraOperations operations = mock();
+		when(operations.getConverter()).thenReturn(converter);
+
+		var contributor = CassandraRepositoryFragmentsContributor.DEFAULT
+				.andThen(MyCassandraRepositoryFragmentsContributor.INSTANCE)
+				.andThen(MyOtherCassandraRepositoryFragmentsContributor.INSTANCE);
+
+		var fragments = contributor.contribute(
+				AbstractRepositoryMetadata.getMetadata(MyUserRepo.class),
+				new MappingCassandraEntityInformation<>(mappingContext.getPersistentEntity(User.class), converter),
+				operations);
+
+		assertThat(fragments.stream())
+				.extracting(RepositoryFragment::getImplementationClass)
+				.containsExactly(Optional.of(MyFragment.class), Optional.of(MyOtherFragment.class));
+	}
+
+	enum MyCassandraRepositoryFragmentsContributor implements CassandraRepositoryFragmentsContributor {
+
+		INSTANCE;
+
+		@Override
+		public RepositoryFragments contribute(RepositoryMetadata metadata,
+				CassandraEntityInformation, ?> entityInformation, CassandraOperations operations) {
+			return RepositoryFragments.just(new MyFragment());
+		}
+
+		@Override
+		public RepositoryFragments describe(RepositoryMetadata metadata) {
+			return RepositoryFragments.just(new MyFragment());
+		}
+	}
+
+	enum MyOtherCassandraRepositoryFragmentsContributor implements CassandraRepositoryFragmentsContributor {
+
+		INSTANCE;
+
+		@Override
+		public RepositoryFragments contribute(RepositoryMetadata metadata,
+				CassandraEntityInformation, ?> entityInformation, CassandraOperations operations) {
+			return RepositoryFragments.just(new MyOtherFragment());
+		}
+
+		@Override
+		public RepositoryFragments describe(RepositoryMetadata metadata) {
+			return RepositoryFragments.just(new MyOtherFragment());
+		}
+	}
+
+	static class MyFragment {
+
+	}
+
+	static class MyOtherFragment {
+
+	}
+
+	interface MyUserRepo extends Repository