Gracias, me alegra de que te guste el contenido! En verdad no hay mucho mas que comentar, antiguamente se hacian cosas como Maybe pero con nullables ya no hace falta. al final este "patron" te muestra una guia a seguir, pero la ampliación depende más de lo que tú utilices, y lo que tu código requiera que lo expandas. un saludo!
excelente aporte, a mi lo que me gusta usar en lugar de tener una lista de errores o ifs para validar cada una de las propiedades del User es value objects. En ese caso tendrías un value object UserName por ejemplo que valide si ese valor es correcto
Espectacular aporte y muy buenas explicación!!!. Seria genial en la medida de tus posibilidades poder hacer mas videos de buenas practicas con este patron en una api rest con Net Core. Saludos cordiales desde Argentina!!! y un buen 2021 a toda la comunidad que sigue tu canal
Gracias por este video, He venido aplicando mi propia version de Railway sin saberlo y no tan compacto y óptimo como este. De ahora en adelante lo aplicaré de esta forma, creo que vale mucho la pena. Por otro lado, entendí en el video que dejarias el enlace de Github, pero no lo veo...
No estoy en casa ahora, en el primer comentario debería haber un enlace al blog, y en el blog tienes el enlace a GitHub. Aún así si buscas mi usuario "/Electnewt" en GitHub es un repo llamado ejemploROP (creo recordar )
Hola! Me alegro de que te guste! Para profundizar en el patrón deberás probarlo y utilizarlo por ti mismo. Lo puedes encontrar en nuget por "NetMentor.ROP", si tienes dudas no dudes en preguntar, y si encuentras algún bug aquí tienes el GitHub para indicar el problema github.com/ElectNewt/EjemploRop Por supuesto si quieres añadir alguna funcionalidad será bienvenida :D Un saludo
Vi este video hace 1 año y medio masomenos cuando recien arrancaba mi carrera profesional y te juro que no entendi nada. Hoy por fin entiendo este y la gran mayoria de tus videos jaja. Mi pregunta va mas orientada a si bien ahora entiendo este tipo de abstracciones me cuesta mucho identificar cuando un problema debe ser abstraido y como implementar esa abstraccion. Hay alguna recomendacion para eso? O eso solo te lo da el tiempo y la experiencia. Gracias Ivan como siempre por todo el contenido que creas
hay una "regla" no escrita que suele decir que las funcionalidades se abstraen después de tenerlas repetidas 3 veces. la realidad es al gusto de cada uno, y lo complejo que sea, no es lo mismo abstraer una funcionalidad que pueden ser 200 líneas, que una de 5; Poco a poco en el trabajo te vas dando cuenta de lo que se repite constantemente y tal; así que sí, experiencia posiblemente haha.
La verdad es que es un buen tema para hacer un vídeo y más ahora que con .net5 tendremos los records. Hay varios motivos (siempre aplicados a una idea de un proyecto mayor, en el ejemplo esta reducido al mínimo) pero principalmente son los siguientes: Cuando utilizamos struct estamos evitando cualquier tipo de herencia. y un struct NO puede ser null, esto junto a que no puede tener constructor por defecto (debemos crear el nuestro propio) nos asegura que NUNCA vamos a tener una nullReferenceException ya bien sea en la propia variable rel Result como en sus hijos .value o .Errors
@@NetMentor Buenas. Antes que nada permite que te felicite por tu trabajo y labor. Lo cierto es que me he realizado la misma pregunta que Alejandro y, a la vista de la respuesta que bien me parece coherente me pregunto si no sería más eficiente declarar "Result" como una clase sellada y utilizar la directiva de preprocesador "#nullable enable" (no evitaría realmente que una variable de este tipo almacenará el "valor null", pero si que nos lanzaría una advertencia). Según tengo entendido las estructuras son realmente objetos que se almacenan en la pila, por lo que cada vez que ejecutamos un método (Bind, Then, Map) generamos una copia de este objeto para facilitarsela al método en cuestión, duplicando de esta manera el objeto al completo en la pila mientras dure la ejecución de dicho método. Si esto fuera así, al declarar "Result" como una clase sellada y para la ejecución de los citados métodos únicamente duplicaríamos la referencia al objeto. Entiendo que la ganancia real sería mínima en la mayoría de los casos (dependería realmente de lo que almacene el tipo T en cada caso) y que es por este motivo que muy probablemente sea mejor tan sólo declarar "Result" como una estructura. Pero me gustaría saber si en tu opinión y ante el diseño de un proyecto en el que "T" fueran a corresponderse con objetos considerablemente grandes sería correcto y/o beneficioso declarar "Result" como una clase sellada. PD: Es bastante probable que pueda estar equivocado en alguna de las suposiciones realizadas. Si así fuera ruego disculpes las molestias. ¡Muchísimas gracias!
Hola! vamos por partes que es bastante largo el comentario haha. Sobre nullable, si de ehcho pensaba que tenia el enable en el csproj, pero he comprobado y parece que no :/ de hecho estaba convencidisimo 🤦♂️ de todas formas, antes de "nullable" se utilizaba el tipo Maybe, que basicamente daba una excepción si algo era null, es una práctica común en programación funcional Pero el mi comentario previo me referia a Result siendo null, no a T Sobre struct y class (y record) quería hacer un vídeo, pero es algo mas complejo que eso únicamente, haber como me explico, Result al ser un struct se queda en el stack y no va al heap, pero si T es una classe, esa si va al heap. (solo T no Result) La cosa es que el stack se limpia al salir de la función de forma automatica (en c#, en otros lenguajes no), y el motivo por el que usamos stuct es ese, creamos una copia, por lo que dejamos de usar el objeto asi que el GC lo puede limipar. En el stack el GC funciona en plan que solo puedes limpiar el ultimo objeto que se ha añadido, osea no puedes limpiar objetos en posiciones aleatorias, tiene que ser en orden. en el heap puedes limpiar lo que quieras. Por lo tanto al pasar de un metodo a otro (usando bind) limpiamos Result pero no T ya que T va a ser usado en el método siguiente. un struct dentro de una clase funciona de forma diferente, en ese caso sí se ubica en el heap. Espero que haya quedado claro, porque es algo lioso, un saludo!
@@NetMentor Aclaradisimo, no había caído en que lo que contiene "Result" al fin y al cabo es una referencia a "T" y como tal nunca será un objeto "pesado" en si mismo. Ahora ya si que entiendo los beneficios de definir "Result" como una estructura en lugar de una clase ¡¡Mil gracias!! :D
¿Sería mucho pedir ver un ejemplo de este patrón mediante un API? Realmente me gustaría ver como sería la implementación de una relación 1...* entre modelos de un DTO de esta manera.
Sí, la idea es, la web que estoy haciendo para lo de "web con c#" hacerla siguiendo este patrón, así que sí. Posiblemente durante los próximos 4 veamos algo más utilizando el patrón. Lo único es que aún tengo un que explicar un par de cosas antes de pasar a programar los servicios.
@@NetMentor Sería muy bueno ver eso sinceramente, estuve viendo un par de ejemplos de CRUDS y bueno... Quedé en otro planeta de lo perdido que estoy, pues no sé sí es algo común o obligatorio pero todos los ejemplos que conseguí usan inmutabilidad, la cual aún no comprendo en su totalidad. Además me puse a pensar como sería implementar un automapper así y me explotó la última neurona que me quedaba.
@@AbrahamJLR A que te refieres con que usan inmutabilidad, inmutabilidad es lo normal, pero dentro de un objeto, si necesitas hacer una query para consultar cierto dato, o cambiar de un objeto a otro no suele haber ningún problema. Pero si, la web será una web completa, e ire haciendo "paso a paso" en el canal así que espero que pueda resolver tus dudas. :)
@@NetMentor Tengo un pequeño dilema con la inmutabilidad, quizá no capté bien el concepto pues hasta ahora tenía entendido que en los principales lugares donde puede ser útil eran únicamente en escenarios multi-threading, y al hacer uso de la programación funcional, pero al ver tus vídeos he entendido que más allá de eso también influye en el mantenimiento del código, siendo más fácil de leer y a su vez presenta una estructura que quizá te quita uno que otro error. Lástimosamente la última vez que intenté hacer usa de esta transformando una pequeña API me ví con un centenar de erorres, imagino que hasta los métodos del CRUD deben ser distintos, pues cada intento de request era un error nuevo. :') Disculpa sí quizá mi pregunta y comentarios están mal planteados, pues realmente presento dudas en el tema.
En multithreading es recomendable principalemnte porque si tienes Precio.Impuesto = await GetImpuesto() es posible, que en otro hilo estes accediendo a `Precio.Impuesto` mientras `GetImpuesto()` se esta ejecutando, ese entre muchos problemas, pero basicamente ese es un ejemplo claro. Respecto a tu problema en concreto, deberias mas más datos o pasar un github con el error y te lo puedo mirar, pero sin mas información es imposible. dicho esto, Recomiendo tamiben empezar con la idea de que el código sea inmutable, no migrar uno mutable, ya que normalmente en los mutables no se siguen muchas normas que debemos utilizar para realizar códigos inmutables.
Blog: www.netmentor.es/Entrada/railway-oriented-programming
Twitter: twitter.com/NetMentorTW
me encantaria que hablaras profundamente de esto, tu canal es oro puro.
Gracias, me alegra de que te guste el contenido!
En verdad no hay mucho mas que comentar, antiguamente se hacian cosas como Maybe pero con nullables ya no hace falta.
al final este "patron" te muestra una guia a seguir, pero la ampliación depende más de lo que tú utilices, y lo que tu código requiera que lo expandas.
un saludo!
Muy jefe, es demasiado pro en algunos aspectos. Me he quedado con la idea general, pero tengo que volver a ver el video
excelente aporte, a mi lo que me gusta usar en lugar de tener una lista de errores o ifs para validar cada una de las propiedades del User es value objects. En ese caso tendrías un value object UserName por ejemplo que valide si ese valor es correcto
Espectacular aporte y muy buenas explicación!!!. Seria genial en la medida de tus posibilidades poder hacer mas videos de buenas practicas con este patron en una api rest con Net Core. Saludos cordiales desde Argentina!!! y un buen 2021 a toda la comunidad que sigue tu canal
Excelente, gracias por compartir tus conocimientos
excelente, ya espero un ejemplo con web API tal vez con una arquitectura como DDD quedaría muy chulo!)
Saludos!)
podrias extender el blog, explicando en que casos utilizar then, ignore, traverse, etc y como utilizar unit
La verdad es que es buena idea, más que en el blog, en el propio repo de GitHub ya que voy ampliando el código cuando necesito algo nuevo.
Gracias por este video, He venido aplicando mi propia version de Railway sin saberlo y no tan compacto y óptimo como este. De ahora en adelante lo aplicaré de esta forma, creo que vale mucho la pena. Por otro lado, entendí en el video que dejarias el enlace de Github, pero no lo veo...
No estoy en casa ahora, en el primer comentario debería haber un enlace al blog, y en el blog tienes el enlace a GitHub. Aún así si buscas mi usuario "/Electnewt" en GitHub es un repo llamado ejemploROP (creo recordar )
Muy buen video, muchas gracias por el detalle. como puedo profudizar y ver mas cosas sobre este nuevo patron?. gracias de antemano por tu atención.
Hola! Me alegro de que te guste! Para profundizar en el patrón deberás probarlo y utilizarlo por ti mismo. Lo puedes encontrar en nuget por "NetMentor.ROP", si tienes dudas no dudes en preguntar, y si encuentras algún bug aquí tienes el GitHub para indicar el problema github.com/ElectNewt/EjemploRop
Por supuesto si quieres añadir alguna funcionalidad será bienvenida :D
Un saludo
Vi este video hace 1 año y medio masomenos cuando recien arrancaba mi carrera profesional y te juro que no entendi nada. Hoy por fin entiendo este y la gran mayoria de tus videos jaja. Mi pregunta va mas orientada a si bien ahora entiendo este tipo de abstracciones me cuesta mucho identificar cuando un problema debe ser abstraido y como implementar esa abstraccion. Hay alguna recomendacion para eso? O eso solo te lo da el tiempo y la experiencia. Gracias Ivan como siempre por todo el contenido que creas
hay una "regla" no escrita que suele decir que las funcionalidades se abstraen después de tenerlas repetidas 3 veces. la realidad es al gusto de cada uno, y lo complejo que sea, no es lo mismo abstraer una funcionalidad que pueden ser 200 líneas, que una de 5;
Poco a poco en el trabajo te vas dando cuenta de lo que se repite constantemente y tal; así que sí, experiencia posiblemente haha.
Gracias por compartir
Una pregunta
¿por que usas una struct an lugar de una clase en: public struct Result{ ... ?
La verdad es que es un buen tema para hacer un vídeo y más ahora que con .net5 tendremos los records.
Hay varios motivos (siempre aplicados a una idea de un proyecto mayor, en el ejemplo esta reducido al mínimo) pero principalmente son los siguientes:
Cuando utilizamos struct estamos evitando cualquier tipo de herencia.
y un struct NO puede ser null, esto junto a que no puede tener constructor por defecto (debemos crear el nuestro propio) nos asegura que NUNCA vamos a tener una nullReferenceException ya bien sea en la propia variable rel Result como en sus hijos .value o .Errors
@@NetMentor interesante, gracias por la aclaracion, Saludos!
@@NetMentor Buenas. Antes que nada permite que te felicite por tu trabajo y labor.
Lo cierto es que me he realizado la misma pregunta que Alejandro y, a la vista de la respuesta que bien me parece coherente me pregunto si no sería más eficiente declarar "Result" como una clase sellada y utilizar la directiva de preprocesador "#nullable enable" (no evitaría realmente que una variable de este tipo almacenará el "valor null", pero si que nos lanzaría una advertencia).
Según tengo entendido las estructuras son realmente objetos que se almacenan en la pila, por lo que cada vez que ejecutamos un método (Bind, Then, Map) generamos una copia de este objeto para facilitarsela al método en cuestión, duplicando de esta manera el objeto al completo en la pila mientras dure la ejecución de dicho método. Si esto fuera así, al declarar "Result" como una clase sellada y para la ejecución de los citados métodos únicamente duplicaríamos la referencia al objeto.
Entiendo que la ganancia real sería mínima en la mayoría de los casos (dependería realmente de lo que almacene el tipo T en cada caso) y que es por este motivo que muy probablemente sea mejor tan sólo declarar "Result" como una estructura. Pero me gustaría saber si en tu opinión y ante el diseño de un proyecto en el que "T" fueran a corresponderse con objetos considerablemente grandes sería correcto y/o beneficioso declarar "Result" como una clase sellada.
PD: Es bastante probable que pueda estar equivocado en alguna de las suposiciones realizadas. Si así fuera ruego disculpes las molestias.
¡Muchísimas gracias!
Hola!
vamos por partes que es bastante largo el comentario haha.
Sobre nullable, si de ehcho pensaba que tenia el enable en el csproj, pero he comprobado y parece que no :/ de hecho estaba convencidisimo 🤦♂️ de todas formas, antes de "nullable" se utilizaba el tipo Maybe, que basicamente daba una excepción si algo era null, es una práctica común en programación funcional
Pero el mi comentario previo me referia a Result siendo null, no a T
Sobre struct y class (y record) quería hacer un vídeo, pero es algo mas complejo que eso únicamente, haber como me explico, Result al ser un struct se queda en el stack y no va al heap, pero si T es una classe, esa si va al heap. (solo T no Result)
La cosa es que el stack se limpia al salir de la función de forma automatica (en c#, en otros lenguajes no), y el motivo por el que usamos stuct es ese, creamos una copia, por lo que dejamos de usar el objeto asi que el GC lo puede limipar.
En el stack el GC funciona en plan que solo puedes limpiar el ultimo objeto que se ha añadido, osea no puedes limpiar objetos en posiciones aleatorias, tiene que ser en orden. en el heap puedes limpiar lo que quieras.
Por lo tanto al pasar de un metodo a otro (usando bind) limpiamos Result pero no T ya que T va a ser usado en el método siguiente.
un struct dentro de una clase funciona de forma diferente, en ese caso sí se ubica en el heap.
Espero que haya quedado claro, porque es algo lioso, un saludo!
@@NetMentor Aclaradisimo, no había caído en que lo que contiene "Result" al fin y al cabo es una referencia a "T" y como tal nunca será un objeto "pesado" en si mismo. Ahora ya si que entiendo los beneficios de definir "Result" como una estructura en lugar de una clase ¡¡Mil gracias!! :D
¿Sería mucho pedir ver un ejemplo de este patrón mediante un API? Realmente me gustaría ver como sería la implementación de una relación 1...* entre modelos de un DTO de esta manera.
Sí, la idea es, la web que estoy haciendo para lo de "web con c#" hacerla siguiendo este patrón, así que sí. Posiblemente durante los próximos 4 veamos algo más utilizando el patrón. Lo único es que aún tengo un que explicar un par de cosas antes de pasar a programar los servicios.
@@NetMentor Sería muy bueno ver eso sinceramente, estuve viendo un par de ejemplos de CRUDS y bueno... Quedé en otro planeta de lo perdido que estoy, pues no sé sí es algo común o obligatorio pero todos los ejemplos que conseguí usan inmutabilidad, la cual aún no comprendo en su totalidad. Además me puse a pensar como sería implementar un automapper así y me explotó la última neurona que me quedaba.
@@AbrahamJLR A que te refieres con que usan inmutabilidad, inmutabilidad es lo normal, pero dentro de un objeto, si necesitas hacer una query para consultar cierto dato, o cambiar de un objeto a otro no suele haber ningún problema.
Pero si, la web será una web completa, e ire haciendo "paso a paso" en el canal así que espero que pueda resolver tus dudas. :)
@@NetMentor Tengo un pequeño dilema con la inmutabilidad, quizá no capté bien el concepto pues hasta ahora tenía entendido que en los principales lugares donde puede ser útil eran únicamente en escenarios multi-threading, y al hacer uso de la programación funcional, pero al ver tus vídeos he entendido que más allá de eso también influye en el mantenimiento del código, siendo más fácil de leer y a su vez presenta una estructura que quizá te quita uno que otro error. Lástimosamente la última vez que intenté hacer usa de esta transformando una pequeña API me ví con un centenar de erorres, imagino que hasta los métodos del CRUD deben ser distintos, pues cada intento de request era un error nuevo. :') Disculpa sí quizá mi pregunta y comentarios están mal planteados, pues realmente presento dudas en el tema.
En multithreading es recomendable principalemnte porque si tienes
Precio.Impuesto = await GetImpuesto()
es posible, que en otro hilo estes accediendo a `Precio.Impuesto` mientras `GetImpuesto()` se esta ejecutando, ese entre muchos problemas, pero basicamente ese es un ejemplo claro.
Respecto a tu problema en concreto, deberias mas más datos o pasar un github con el error y te lo puedo mirar, pero sin mas información es imposible.
dicho esto, Recomiendo tamiben empezar con la idea de que el código sea inmutable, no migrar uno mutable, ya que normalmente en los mutables no se siguen muchas normas que debemos utilizar para realizar códigos inmutables.