Al igual que con cualquier regla, creo que lo importante aquí es considerar el propósito de la regla, el espíritu, y no verse atrapado en analizar exactamente cómo se redactó la regla en algún libro de texto y cómo aplicarla en este caso. No necesitamos abordar esto como abogados. El propósito de las reglas es ayudarnos a escribir mejores programas. No es que el propósito de escribir programas sea mantener las reglas.
El propósito de la regla de responsabilidad única es hacer que los programas sean más fáciles de entender y mantener haciendo que cada función haga una cosa coherente y autónoma.
Por ejemplo, una vez escribí una función a la que llamé algo así como "checkOrderStatus", que determinaba si un pedido estaba pendiente, enviado, pedido en espera, lo que sea, y devolvió un código que indica cuál. Luego apareció otro programador y modificó esta función para actualizar también la cantidad disponible cuando se envió el pedido. Esto violó severamente el principio de responsabilidad única. Otro programador que lea ese código más tarde vería el nombre de la función, vería cómo se utilizó el valor de retorno y bien podría nunca sospechar que realizó una actualización de la base de datos. Alguien que necesitara obtener el estado del pedido sin actualizar la cantidad disponible estaría en una posición incómoda: ¿debería escribir una nueva función que duplique la parte del estado del pedido? ¿Agregar una bandera para indicarle si se debe hacer la actualización de db? Etc.
Por otro lado, no quisiera señalar lo que constituye "dos cosas". Recientemente escribí una función que envía información del cliente desde nuestro sistema al sistema de nuestro cliente. Esa función realiza un reformateo de los datos para cumplir con sus requisitos. Por ejemplo, tenemos algunos campos que pueden ser nulos en nuestra base de datos, pero no permiten nulos, por lo que tenemos que completar un texto falso, "no especificado" o me olvido de las palabras exactas. Podría decirse que esta función está haciendo dos cosas: formatear los datos Y enviarlos. Pero deliberadamente pongo esto en una sola función en lugar de tener "reformatear" y "enviar" porque no quiero enviar nunca sin reformatear. No quiero que alguien escriba una nueva llamada y no me dé cuenta de que tiene que llamar a reformatear y luego enviar.
En su caso, actualizar la base de datos y devolver una imagen del registro escrito parecen dos cosas que bien podrían ir juntas de manera lógica e inevitable. No conozco los detalles de su aplicación, así que no puedo decir definitivamente si es una buena idea o no, pero parece plausible.
Si está creando un objeto en la memoria que contiene todos los datos para el registro, realiza las llamadas a la base de datos para escribir esto y luego devuelve el objeto, esto tiene mucho sentido. Tienes el objeto en tus manos. ¿Por qué no simplemente devolverlo? Si no devolvió el objeto, ¿cómo lo obtendría la persona que llama? ¿Tendría que leer la base de datos para obtener el objeto que acaba de escribir? Eso parece bastante ineficiente. ¿Cómo encontraría el registro? ¿Conoces la clave primaria? Si alguien declara que es "legal" que la función de escritura devuelva la clave primaria para que pueda volver a leer el registro, ¿por qué no simplemente devolver todo el registro para que no tenga que hacerlo? ¿Cual es la diferencia?
Por otro lado, si crear el objeto es un trabajo bastante distinto de escribir el registro de la base de datos, y una persona que llama podría querer escribir pero no crear el objeto, entonces esto podría ser un desperdicio. Si una persona que llama puede querer el objeto pero no hace la escritura, entonces tendría que proporcionar otra forma de obtener el objeto, lo que podría significar escribir código redundante.
Pero creo que el escenario 1 es más probable, así que diría, probablemente no hay problema.