diff --git a/typo3/sysext/extbase/Classes/Property/TypeConverter/ObjectConverter.php b/typo3/sysext/extbase/Classes/Property/TypeConverter/ObjectConverter.php
index 3f31ff56ccb4f4282130ecf189b7e47a54a46acf..bb0c2a94dd39f6693493bac05a9bdee00b9a780c 100644
--- a/typo3/sysext/extbase/Classes/Property/TypeConverter/ObjectConverter.php
+++ b/typo3/sysext/extbase/Classes/Property/TypeConverter/ObjectConverter.php
@@ -18,6 +18,7 @@ declare(strict_types=1);
 namespace TYPO3\CMS\Extbase\Property\TypeConverter;
 
 use Psr\Container\ContainerInterface;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject;
 use TYPO3\CMS\Extbase\Object\Container\Container;
 use TYPO3\CMS\Extbase\Property\Exception\InvalidDataTypeException;
@@ -129,7 +130,7 @@ class ObjectConverter extends AbstractTypeConverter
      * @param string $propertyName
      * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
      * @return string
-     * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
+     * @throws InvalidTargetException
      * @internal only to be used within Extbase, not part of TYPO3 Core API.
      */
     public function getTypeOfChildProperty(string $targetType, string $propertyName, PropertyMappingConfigurationInterface $configuration): string
@@ -183,7 +184,7 @@ class ObjectConverter extends AbstractTypeConverter
      * @param array $convertedChildProperties
      * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
      * @return object|null the target type
-     * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
+     * @throws InvalidTargetException
      * @internal only to be used within Extbase, not part of TYPO3 Core API.
      */
     public function convertFrom($source, string $targetType, array $convertedChildProperties = [], PropertyMappingConfigurationInterface $configuration = null): ?object
@@ -242,41 +243,82 @@ class ObjectConverter extends AbstractTypeConverter
 
     /**
      * Builds a new instance of $objectType with the given $possibleConstructorArgumentValues. If
-     * constructor argument values are missing from the given array the method
-     * looks for a default value in the constructor signature. Furthermore, the constructor arguments are removed from $possibleConstructorArgumentValues
+     * constructor argument values are missing from the given array the method looks for a default
+     * value in the constructor signature. Furthermore, the constructor arguments are removed from
+     * $possibleConstructorArgumentValues: They are considered "handled" by __construct and will
+     * not be mapped calling setters later.
      *
      * @param array $possibleConstructorArgumentValues
      * @param string $objectType
      * @return object The created instance
-     * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException if a required constructor argument is missing
+     * @throws InvalidTargetException if a required constructor argument is missing
      */
     protected function buildObject(array &$possibleConstructorArgumentValues, string $objectType): object
     {
-        if (empty($possibleConstructorArgumentValues) && $this->container->has($objectType)) {
+        // The ObjectConverter typically kicks in, if request arguments are to be mapped to
+        // a domain model. An example is ext:belog:Domain/Model/Demand.
+        // Domain models are data objects and should thus be fetched via makeInstance(), should
+        // not be registered as service, and should thus not be DI aware.
+        // However, historically, ObjectManager->get() made *all* classes DI aware.
+        // Additionally, all to-be-mapped arguments are hand over as "possible constructor arguments" here,
+        // and extbase is able to use single arguments as constructor arguments to domain models,
+        // if a __construct() with an argument having the same name as a to-be-mapped argument exists.
+        // This is the reason that &$possibleConstructorArgumentValues is hand over as reference here:
+        // If an argument can be hand over as constructor argument, it is considered "already mapped" and
+        // is not manually mapped calling setters later.
+        // To be as backwards compatible as possible, without using ObjectManager, the following logic
+        // is applied for now:
+        // * If the class is registered as service (container->has()=true), and if there are no
+        //   $possibleConstructorArgumentValues, instantiate the class via container->get(). Easy
+        //   scenario - the target class is DI aware and will get dependencies injected. A different target
+        //   class can be specified using service configuration if needed.
+        // * If the class is registered as service, and if there are $possibleConstructorArgumentValues,
+        //   the class is instantiated via container->get(). $possibleConstructorArgumentValues are *not* hand
+        //   over to the constructor. The target class can then use constructor injection and inject* methods
+        //   for DI. A different target class can be specified using service configuration if needed. Mapping
+        //   of arguments is done using setters by follow-up code.
+        // * If the class is *not* registered as service, makeInstance() is used for object retrieval.
+        // * @todo delete in v12: As compat layer, if a different implementation has been registered
+        //   for the ObjectManager (extbase Container->registerImplementation()), the ObjectManager target class is
+        //   still used in v11, but this is marked deprecated, with makeInstance(), a different implementation should
+        //   be registered as XCLASS if really needed.
+        // * If there are no $possibleConstructorArgumentValues, makeInstance() is used right away.
+        // * If there are $possibleConstructorArgumentValues and __construct() does not exist, makeInstance()
+        //   is used without constructor arguments. Mapping of argument values via setters is done by follow-up code.
+        // * If there are $possibleConstructorArgumentValues and if __construct() exists, extbase reflection
+        //   is used to map single arguments to constructor arguments with the same name and
+        //   makeInstance() is used to instantiate the class. Mapping remaining arguments is done by follow-up code.
+        if ($this->container->has($objectType)) {
+            // @todo: consider dropping container->get() to prevent domain models being treated as services in >=v12.
             return $this->container->get($objectType);
         }
 
-        // @deprecated since v11, will be removed in v12: ContainerInterface resolves class names. v12: Drop everything below.
         $specificObjectType = $this->objectContainer->getImplementationClassName($objectType);
-        $classSchema = $this->reflectionService->getClassSchema($specificObjectType);
+        if ($specificObjectType !== $objectType) {
+            // @deprecated since v11, will be removed in v12: makeInstance() overrides should be done as XCLASS
+            trigger_error(
+                'Container->registerImplemenation() for class ' . $objectType . ' is deprecated. Use XCLASS instead.',
+                E_USER_DEPRECATED
+            );
+        }
 
-        if ($classSchema->hasConstructor()) {
-            $constructor = $classSchema->getMethod('__construct');
-            $constructorArguments = [];
-            foreach ($constructor->getParameters() as $parameterName => $parameter) {
-                if (array_key_exists($parameterName, $possibleConstructorArgumentValues)) {
-                    $constructorArguments[] = $possibleConstructorArgumentValues[$parameterName];
-                    unset($possibleConstructorArgumentValues[$parameterName]);
-                } elseif ($parameter->isOptional()) {
-                    $constructorArguments[] = $parameter->getDefaultValue();
-                } else {
-                    throw new InvalidTargetException('Missing constructor argument "' . $parameterName . '" for object of type "' . $objectType . '".', 1268734872);
-                }
+        if (empty($possibleConstructorArgumentValues) || !method_exists($specificObjectType, '__construct')) {
+            return GeneralUtility::makeInstance($specificObjectType);
+        }
+
+        $classSchema = $this->reflectionService->getClassSchema($specificObjectType);
+        $constructor = $classSchema->getMethod('__construct');
+        $constructorArguments = [];
+        foreach ($constructor->getParameters() as $parameterName => $parameter) {
+            if (array_key_exists($parameterName, $possibleConstructorArgumentValues)) {
+                $constructorArguments[] = $possibleConstructorArgumentValues[$parameterName];
+                unset($possibleConstructorArgumentValues[$parameterName]);
+            } elseif ($parameter->isOptional()) {
+                $constructorArguments[] = $parameter->getDefaultValue();
+            } else {
+                throw new InvalidTargetException('Missing constructor argument "' . $parameterName . '" for object of type "' . $objectType . '".', 1268734872);
             }
-            // @deprecated since v11, will be removed in v12
-            return $this->objectManager->get(...[$objectType, ...$constructorArguments]);
         }
-        // @deprecated since v11, will be removed in v12
-        return $this->objectManager->get($objectType);
+        return GeneralUtility::makeInstance(...[$specificObjectType, ...$constructorArguments]);
     }
 }