(cc) Foto por Rob Halsell

Después del post inicial de presentación, ahora viene el rollete con los detalles técnicos de cómo he montado esta web, qué tecnologías y servicios hay detrás y demás tecnicismos frikis. ¡Avisado quedas!

La web se divide en dos aplicaciones: Páginas estáticas y el Blog.

Las páginas estáticas

Tanto la pantalla de Inicio, como la parte de Proyectos y la Biografía las he implementado mediante ASP.NET MVC 5.

Dispone de un pequeño Backend, para almacenar los datos de los Proyectos, Clientes, Imágenes, etc. para el que utilizo sencillos archivos XML que deserializo a objetos DTO (Data Transfer Object) que luego manipulo con Linq. El coste de la deserialización de estos archivos está entre 5 y 10ms, que es despreciable, y que además cacheo en memoria, por lo que no supone una penalización mayor que tenerlos en una BD relacional al uso. De hecho, es apreciablemente más rápido y barato, y el volumen de datos que manejo es relativamente pequeño.

A modo de ejemplo, esta es una muestra del archivo que almacena las categorías de proyectos:

<?xml version="1.0" encoding="utf-8" ?>
<ArrayOfDtoCategory>
    <DtoCategory>
        <Id>1</Id>
        <Name><![CDATA[E-Learning]]></Name>
    </DtoCategory>
    <DtoCategory>
        <Id>2</Id>
        <Name><![CDATA[WPO]]></Name>
    </DtoCategory>
</ArrayOfDtoCategory>

y este, un fragmento resumido de la clase C# que lo deserializa:

[Serializable]
[DataContract(Name = "DtoCategory", Namespace = "DTO.Projects")]
public class DtoCategory
{
	#region " Atributos "

	/// <summary>
	/// Identificador de base de datos
	/// </summary>
	private Int32 id = -1;

	/// <summary>
	/// Nombre de la categoría
	/// </summary>
	private String name;

	#endregion

	#region " Propiedades "

	/// <summary>
	/// Obtiene o establece "Identificador de base de datos"
	/// </summary>
	/// <value><see cref="Int32"/>Obtiene o establece "Identificador de base de datos"</value>
	[DataMember]
	public virtual Int32 Id
	{
		get { return id; }
		set { id = value; }
	}

	/// <summary>
	/// Obtiene o establece "Nombre de la categoría"
	/// </summary>
	/// <value><see cref="String"/>Obtiene o establece "Nombre de la categoría"</value>
	[DataMember]
	public String Name
	{
		get { return name; }
		set { name = value; }
	}

	#endregion
}

Una vez dispongo del modelo de datos modelado y los archivos XML con la información controlados, desarrollo las 4 vistas principales del proyecto, utilizando Razor como View engine y Bootstrap + el template Unify para el diseño y la implementación del frontend.

El blog

Para montar el blog me decidí a utilizar WordPress, porque no tiene sentido reinventar la rueda y ponerme a programar un gestor de blogs desde cero, y porque últimamente he estado trabajando con esta plataforma por motivos profesionales y le estoy cogiendo el tranquillo, además de para seguir aprendiendo y probando cosas nuevas para futuros proyectos.

Como era importante para mi mantener una uniformidad visual, decidí utilizar el mismo tema que para el resto de la web, por lo que me tocó adaptar el tema a WordPress, ya que en origen no viene preparado para ello.

Como curiosidad, comentaré que el blog está incluido dentro de la misma solución de Visual Studio que implementa la web, en una carpeta /blog, ya que me facilita las cosas a la hora de hacer el deploy del site a Azure desde el propio IDE, además de ahorrar costes de hosting porque sólo necesito un site. Pero para que esto funcione y convivan ambas tecnologías (ASP.NET MVC 5 y PHP) hay que hacer un par de retoques, para que cada una enrute sus peticiones y no interfiera en las de la otra.

En mi caso, utilizo como alojamiento Azure Websites, por lo que mi servidor web es un IIS ejecutando PHP 5.5.

Por una parte, en el archivo Web.config común hay que incluir la regla de enrutamiento de WordPress:

<system.webserver>
	<rewrite>
		<rules>
			<rule name="blog" patternSyntax="Wildcard">
				<match url="blog/*"/>
				<conditions>
					<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
					<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
				</conditions>
				<action type="Rewrite" url="index.php"/>
			</rule>
		</rules>
	</rewrite>
</system.webserver>

Importante comprobar como sólo enrutamos las peticiones a blog/*. De esta manera, el motor de PHP ignora las peticiones que no vayan explícitamente al blog y se las deja a MVC. Igualmente es importante cambiar el nombre de la regla, de «wordpress» (por defecto) a otro (en mi caso, «blog»). Esto es debido a que es muy probable que tengamos otro archivo web.config dentro del directorio del blog que utilice los valores por defecto y esto crea un conflicto en IIS que generará un error y el blog dejará de funcionar.

Por otro lado, es importante decirle a MVC que no enrute las peticiones al blog, para que las maneje PHP. Esto se consigue añadiendo la siguiente línea antes del código de definición de las rutas de nuestra aplicación:

routes.IgnoreRoute("blog/{*pathInfo}");

Los servicios

Para el alojamiento utilizo 1 Azure Website, que comparten tanto el site en MVC 5 como el blog en WordPress, así comparto gastos de hosting.

Para mejorar el rendimiento, utilizo CloudFlare como proxy DNS, que me proporciona servicios de seguridad y optimización automáticos de forma transparente, y Varnish como servidor proxy-caché, que tengo instalado en una máquina virtual Extra pequeña con Ubuntu, también sobre Azure. La unión de estos dos servicios reduce enormemente el número de peticiones que recibe el sitio web, por lo que es suficiente con una instancia compartida del Website para mantener el site.

Finalmente, una vez aplicadas las técnicas de optimización WPO al site principal, www.sergigisbert.com, obtenemos la siguiente calificación de GTMetrix:

gtmetrix_web.png

Ver el informe completo

Siempre penalizada por la inclusión de recursos externos a los que no tenemos acceso.

Dudas, aclaraciones, preguntas, sugerencias, ¡aquí me tenéis!