De serverless a less server code

Últimamente estuve probando Firebase, me gustó mucho la idea de poder hacerte una aplicación frontend (html+javascript) y tener un modo fácil gestionar la autenticación y tener una base de datos sin mucho esfuerzo. Probé a hacer cosas y la verdad es que era divertido. Pronto se me quedó pequeño y empezaba a ser tosco meter reglas de negocio en el código cliente, además de tener mis dudas con respecto a la seguridad de los datos.

En muchas ocasiones he empezado con un concepto o una idea y la mayoría de las veces se ha quedado a medio por que tenía que programar cosas que no tenían nada que ver con la idea inicial, mucho boilerplate, así que perseguía algo para poder montar aplicaciones chorras y pruebas de forma fácil. Si la idea va a más pues ya se vería como se escalaría pero necesitaba algo con lo que poder empezar rápido.

Así pues se me ocurrió que estaría muy bien poder montarte de forma rápida una aplicación de servidor que con el mínimo código posible te diera acceso a una base de datos. Algo que con poca inicialización, por ejemplo:

    app.UseDataBaseAccess(connectionstring, configuration);

Te diera la posibilidad de que desde el cliente JavaScript, pudieras comunicar con la base de datos con algo como esto:

    dbAccess.Table("Products").Query({name: "CocaCola"}).then((result) => blabla)
    dbAccess.Table("Products").Insert({name: "Pepsi"}).then((result) => blabla)

Para meter reglas de negocio en el servidor se me ocurría que podría ser algo así:

    public void beforeInsertPost(dynamic post) {
        post.userId = _currentUser.Id;
    }

He incluso algo así para poder crear la proyección de los datos que se emiten al cliente:

    public void onProjectProduct(dynamic projection) {    
       projection.price = _currentUser.hasRole("admin")? 1: 0;
    }

Bien, la primera dificultad que se me ocurrió fue diseñar una metodología para crear consultas en el cliente e interpretarlas en el servidor, … y esto me llevó a pensar en MongoDb. Puesto que la api de MongoDb acepta objetos para crear los queries, objetos para los inserts y updates, se me ocurrió que desde el cliente podría montar las consultas y en el servidor leerlas como tal y pasárselas a mongo, de forma que se podría hacer algo así en el cliente:

    miJsMongoDbAccess.collection("products").find({ amount: { $gte: 50 } })
        .then((response) => balbla).catch((reason) => blabla);

Pregunté a unos amigos si conocían algo que ya estuviese hecho que hiciera eso, lo pregunté por preguntar, … puesto que ya tenía clavado en la cabeza montarlo yo, pero bueno, si había algo en el mercado me hubiera gustado echarle un vistazo. Como no encontramos nada me tiré a la piscina a montarlo.

Lo primero que debía de hacer era montar un punto de entrada en la aplicación, se me ocurrió crear un controlador MVC con cada uno de los métodos que tienen las colecciones de MongoDb, Find, InsertOne, InserMany, UpdateOne, … montar una ruta así:

    routes.MapRoute(name: "mongo",
                    template: "mongo/{collection}/{action=Find}/{id?}", 
                    defaults: new {controller = "Mongo"});

Y el método del controlador así:

    [HttpPost]
    public object Find(string collectionName, [FromBody] dynamic data)
    {
        var filter = data.filter;
        var order = data.order;
        var projection = data.projection;

        return Mongo.Collection<dinamic>(collectionName)
            .find(filter, order, projection).ToArray();
    }

De tal modo podríamos llamarlo así desde Js:

    $.post("mongo/Products/Find", {
        filter: { amount: { $gte: 50 } },
        projection: { userId: 0 }
    });

Con eso nos bastaría, pero en mi cabeza me ronroneaba el tema de MongoDb y eso junto que en un futuro lo mismo desde el cliente podría querer llamar a otras cosas que no fueran de mongo, como temas de login de usuarios por ejemplo, pensé en hacerlo más versátil.

¿Y si tuviéramos una especie de catalogo de servicios y estos fueran llamados por una acción de un solo controlador?

Una ruta tal que así:

    routes.MapRoute(name: "services",
                    template: "service/{serviceName}/{methodName}/{id?}", 
                    defaults: new {controller = "Service", action = "CallService"});

Y el controlador así:

    [HttpPost]
    public object Find(string serviceName, string methodName, [FromBody] dynamic arguments)
    {
        var service = MagicServiceResolver.ResolveByName(serviceName);

        return MagicMethodInvocator.Invoke(service, methodName, arguments);
    }

Si en la configuración de la aplicación pudiéramos poner algo así:

    app.UseMagicServices(conf => {
        conf.AddService<IMyMagicMongoService>("mongo");
    });

Podríamos llamar al servicio así:

    $.post("service/mongo/Find", {
        collectionName: { Products }
        filter: { amount: { $gte: 50 } },
        projection: { userId: 0 }
    });

Mucho mejor. Por supuesto que IMyMagicMongoService tendría que implementar los métodos de mongo, pero a cambio de esto podría publicar cualquier servicio y ponerlo accesible desde el client-side.

Bien, al proyecto lo he llamado QuickApp, (GitHub/QuickApp), tiene un servicio de acceso a MongoDb, ya admite reglas de negocio a modo de interceptores y también le he implementado un servicio de autenticación. A ver si en breve puedo escribir de como se usa 😉

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

Demuestra que no eres un bot *